From 7a878bdc50741b7a129f44d8cdddcdfc58e7c5db Mon Sep 17 00:00:00 2001 From: KonstantinKondrashov Date: Wed, 6 Sep 2023 19:33:39 +0800 Subject: [PATCH] feat(esp_system): Support IPC_ISR for ESP32P4 --- components/esp_system/Kconfig | 9 +- components/esp_system/include/esp_ipc_isr.h | 39 ++++-- .../include/esp_private/esp_ipc_isr.h | 2 +- components/esp_system/port/CMakeLists.txt | 15 ++- .../port/arch/riscv/esp_ipc_isr_handler.S | 78 +++++++++++ .../port/arch/riscv/esp_ipc_isr_port.c | 48 +++++++ .../port/arch/riscv/esp_ipc_isr_routines.c | 12 ++ .../port/arch/xtensa/esp_ipc_isr_port.c | 36 +++++ .../port/{arch/xtensa => }/esp_ipc_isr.c | 30 +---- .../private/esp_private/esp_ipc_isr_port.h | 39 ++++++ .../main/CMakeLists.txt | 7 +- .../main/port/arch/riscv/test_ipc_isr.c | 29 +++++ .../{ => port/arch/xtensa}/test_ipc_isr.S | 10 +- .../main/test_ipc_isr.c | 28 ++-- components/riscv/vectors_clic.S | 8 +- .../soc/esp32p4/include/soc/hp_system_reg.h | 1 - components/soc/esp32p4/include/soc/soc.h | 1 + docs/docs_not_updated/esp32p4.txt | 1 - docs/en/api-reference/system/index.rst | 2 +- docs/en/api-reference/system/ipc.rst | 123 +++++++++++------- examples/system/.build-test-rules.yml | 16 ++- .../ipc/ipc_isr/{ => riscv}/CMakeLists.txt | 0 examples/system/ipc/ipc_isr/riscv/README.md | 56 ++++++++ .../ipc/ipc_isr/riscv/main/CMakeLists.txt | 3 + .../system/ipc/ipc_isr/riscv/main/callbacks.c | 22 ++++ .../system/ipc/ipc_isr/riscv/main/callbacks.h | 15 +++ examples/system/ipc/ipc_isr/riscv/main/main.c | 43 ++++++ .../ipc/ipc_isr/riscv/pytest_ipc_isr_riscv.py | 20 +++ .../ipc_isr/{ => riscv}/sdkconfig.defaults | 0 .../system/ipc/ipc_isr/xtensa/CMakeLists.txt | 6 + .../system/ipc/ipc_isr/{ => xtensa}/README.md | 2 +- .../ipc_isr/{ => xtensa}/main/CMakeLists.txt | 0 .../ipc/ipc_isr/{ => xtensa}/main/asm_funcs.S | 0 .../ipc/ipc_isr/{ => xtensa}/main/main.c | 20 ++- .../pytest_ipc_isr_xtensa.py} | 2 +- .../ipc/ipc_isr/xtensa/sdkconfig.defaults | 0 tools/ci/check_copyright_ignore.txt | 1 - 37 files changed, 591 insertions(+), 133 deletions(-) create mode 100644 components/esp_system/port/arch/riscv/esp_ipc_isr_handler.S create mode 100644 components/esp_system/port/arch/riscv/esp_ipc_isr_port.c create mode 100644 components/esp_system/port/arch/riscv/esp_ipc_isr_routines.c create mode 100644 components/esp_system/port/arch/xtensa/esp_ipc_isr_port.c rename components/esp_system/port/{arch/xtensa => }/esp_ipc_isr.c (85%) create mode 100644 components/esp_system/port/include/private/esp_private/esp_ipc_isr_port.h create mode 100644 components/esp_system/test_apps/esp_system_unity_tests/main/port/arch/riscv/test_ipc_isr.c rename components/esp_system/test_apps/esp_system_unity_tests/main/{ => port/arch/xtensa}/test_ipc_isr.S (86%) rename examples/system/ipc/ipc_isr/{ => riscv}/CMakeLists.txt (100%) create mode 100644 examples/system/ipc/ipc_isr/riscv/README.md create mode 100644 examples/system/ipc/ipc_isr/riscv/main/CMakeLists.txt create mode 100644 examples/system/ipc/ipc_isr/riscv/main/callbacks.c create mode 100644 examples/system/ipc/ipc_isr/riscv/main/callbacks.h create mode 100644 examples/system/ipc/ipc_isr/riscv/main/main.c create mode 100644 examples/system/ipc/ipc_isr/riscv/pytest_ipc_isr_riscv.py rename examples/system/ipc/ipc_isr/{ => riscv}/sdkconfig.defaults (100%) create mode 100644 examples/system/ipc/ipc_isr/xtensa/CMakeLists.txt rename examples/system/ipc/ipc_isr/{ => xtensa}/README.md (97%) rename examples/system/ipc/ipc_isr/{ => xtensa}/main/CMakeLists.txt (100%) rename examples/system/ipc/ipc_isr/{ => xtensa}/main/asm_funcs.S (100%) rename examples/system/ipc/ipc_isr/{ => xtensa}/main/main.c (74%) rename examples/system/ipc/ipc_isr/{pytest_ipc_isr.py => xtensa/pytest_ipc_isr_xtensa.py} (92%) create mode 100644 examples/system/ipc/ipc_isr/xtensa/sdkconfig.defaults diff --git a/components/esp_system/Kconfig b/components/esp_system/Kconfig index 16ff40306f..e202c51fbb 100644 --- a/components/esp_system/Kconfig +++ b/components/esp_system/Kconfig @@ -530,19 +530,19 @@ menu "ESP System Settings" 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. + Interrupt level to use for Interrupt Watchdog, IPC_ISR 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. + Using level 5 interrupt for Interrupt Watchdog, IPC_ISR 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. + Using level 4 interrupt for Interrupt Watchdog, IPC_ISR and other system checks. endchoice # Insert chip-specific system config @@ -595,11 +595,10 @@ menu "IPC (Inter-Processor Call)" config ESP_IPC_ISR_ENABLE bool - default n if IDF_TARGET_ESP32P4 # TODO: IDF-7769 default y if !FREERTOS_UNICORE help The IPC ISR feature is similar to the IPC feature except that the callback function is executed in the - context of a High Priority Interrupt. The IPC ISR feature is itended for low latency execution of simple + context of a High Priority Interrupt. The IPC ISR feature is intended for low latency execution of simple callbacks written in assembly on another CPU. Due to being run in a High Priority Interrupt, the assembly callbacks must be written with particular restrictions (see "IPC" and "High-Level Interrupt" docs for more details). diff --git a/components/esp_system/include/esp_ipc_isr.h b/components/esp_system/include/esp_ipc_isr.h index 70290ba561..eb6919da9c 100644 --- a/components/esp_system/include/esp_ipc_isr.h +++ b/components/esp_system/include/esp_ipc_isr.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -17,31 +17,44 @@ extern "C" { /** * @brief IPC ISR Callback * - * A callback of this type should be provided as an argument when calling esp_ipc_isr_asm_call() or - * esp_ipc_isr_asm_call_blocking(). + * The callback must be written: + * - in assembly for XTENSA chips (such as ESP32, ESP32S3). + * - in C or assembly for RISCV chips (such as ESP32P4). + * + * A callback of this type should be provided as an argument when calling esp_ipc_isr_call() or + * esp_ipc_isr_call_blocking(). */ typedef void (*esp_ipc_isr_func_t)(void* arg); /** - * @brief Execute an assembly callback on the other CPU + * @brief Execute an ISR callback on the other CPU * * Execute a given callback on the other CPU in the context of a High Priority Interrupt. * * - This function will busy-wait in a critical section until the other CPU has started execution of the callback - * - The callback must be written in assembly, is invoked using a CALLX0 instruction, and has a2, a3, a4 as scratch - * registers. See docs for more details + * - The callback must be written: + * - in assembly for XTENSA chips (such as ESP32, ESP32S3). + * The function is invoked using a CALLX0 instruction and can use only a2, a3, a4 registers. + * See :doc:`IPC in Interrupt Context ` doc for more details. + * - in C or assembly for RISCV chips (such as ESP32P4). * * @note This function is not available in single-core mode. * * @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); +void esp_ipc_isr_call(esp_ipc_isr_func_t func, void* arg) ; /** - * @brief Execute an assembly callback on the other CPU and busy-wait until it completes + * @brief Execute an ISR callback on the other CPU + * See esp_ipc_isr_call(). + */ +#define esp_ipc_isr_asm_call(func, arg) esp_ipc_isr_call(func, arg) + +/** + * @brief Execute an ISR callback on the other CPU and busy-wait until it completes * - * This function is identical to esp_ipc_isr_asm_call() except that this function will busy-wait until the execution of + * This function is identical to esp_ipc_isr_call() except that this function will busy-wait until the execution of * the callback completes. * * @note This function is not available in single-core mode. @@ -49,7 +62,13 @@ void esp_ipc_isr_asm_call(esp_ipc_isr_func_t func, void* arg); * @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); +void esp_ipc_isr_call_blocking(esp_ipc_isr_func_t func, void* arg); + +/** + * @brief Execute an ISR callback on the other CPU and busy-wait until it completes + * See esp_ipc_isr_call_blocking(). + */ +#define esp_ipc_isr_asm_call_blocking(func, arg) esp_ipc_isr_call_blocking(func, arg) /** * @brief Stall the other CPU diff --git a/components/esp_system/include/esp_private/esp_ipc_isr.h b/components/esp_system/include/esp_private/esp_ipc_isr.h index ccdfe1deaa..6a6d9f8cd8 100644 --- a/components/esp_system/include/esp_private/esp_ipc_isr.h +++ b/components/esp_system/include/esp_private/esp_ipc_isr.h @@ -26,7 +26,7 @@ extern "C" { * - This function will register a High Priority Interrupt for a CPU where it is called. The priority of the interrupts is dependent on * the CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL option. * - Callbacks written in assembly can then run in context of the registered High Priority Interrupts - * - Callbacks can be executed by calling esp_ipc_isr_asm_call() or esp_ipc_isr_asm_call_blocking() + * - Callbacks can be executed by calling esp_ipc_isr_call() or esp_ipc_isr_call_blocking() */ void esp_ipc_isr_init(void); diff --git a/components/esp_system/port/CMakeLists.txt b/components/esp_system/port/CMakeLists.txt index 7a323b9ea2..0ad5418428 100644 --- a/components/esp_system/port/CMakeLists.txt +++ b/components/esp_system/port/CMakeLists.txt @@ -17,11 +17,16 @@ if(CONFIG_ESP_CONSOLE_USB_CDC) endif() if(CONFIG_ESP_IPC_ISR_ENABLE) -if(CONFIG_IDF_TARGET_ARCH_XTENSA) - list(APPEND srcs "arch/xtensa/esp_ipc_isr.c" - "arch/xtensa/esp_ipc_isr_handler.S" - "arch/xtensa/esp_ipc_isr_routines.S") -endif() + list(APPEND srcs "esp_ipc_isr.c") + if(CONFIG_IDF_TARGET_ARCH_XTENSA) + list(APPEND srcs "arch/xtensa/esp_ipc_isr_port.c" + "arch/xtensa/esp_ipc_isr_handler.S" + "arch/xtensa/esp_ipc_isr_routines.S") + elseif(CONFIG_IDF_TARGET_ARCH_RISCV) + list(APPEND srcs "arch/riscv/esp_ipc_isr_port.c" + "arch/riscv/esp_ipc_isr_handler.S" + "arch/riscv/esp_ipc_isr_routines.c") + endif() endif() if(CONFIG_IDF_TARGET_ARCH_XTENSA) diff --git a/components/esp_system/port/arch/riscv/esp_ipc_isr_handler.S b/components/esp_system/port/arch/riscv/esp_ipc_isr_handler.S new file mode 100644 index 0000000000..c5a37174c8 --- /dev/null +++ b/components/esp_system/port/arch/riscv/esp_ipc_isr_handler.S @@ -0,0 +1,78 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/hp_system_reg.h" + +/* IPC_ISR handler */ + .equ SAVE_REGS, 16 /* count of saving regs: a0 - a7, t0 - t6, ra */ + .equ CONTEXT_SIZE, (SAVE_REGS * 4) + .section .iram1,"ax" + .global esp_ipc_isr_handler + .type esp_ipc_isr_handler,@function +esp_ipc_isr_handler: + + /* save a0 - a7, t0 - t6, ra */ + addi sp, sp, -(CONTEXT_SIZE) + sw a0, 0(sp) + sw a1, 4(sp) + sw a2, 8(sp) + sw a3, 12(sp) + sw a4, 16(sp) + sw a5, 20(sp) + sw a6, 24(sp) + sw a7, 28(sp) + sw t0, 32(sp) + sw t1, 36(sp) + sw t2, 40(sp) + sw t3, 44(sp) + sw t4, 48(sp) + sw t5, 52(sp) + sw t6, 56(sp) + sw ra, 60(sp) + + /* MIE is cleared, so nested interrupts are disabled */ + + /* Reset isr interrupt flags */ + li a1, HP_SYSTEM_CPU_INT_FROM_CPU_2_REG + csrr a0, mhartid # Get CORE_ID + beqz a0, 1f + li a1, HP_SYSTEM_CPU_INT_FROM_CPU_3_REG +1: + sw zero, (a1) + + /* Set the start flag */ + la a0, esp_ipc_isr_start_fl + sw a0, 0(a0) + + /* Call the esp_ipc_func(void* esp_ipc_func_arg) */ + lw a1, (esp_ipc_func) + lw a0, (esp_ipc_func_arg) + jalr a1 + + /* Set the end flag */ + la a0, esp_ipc_isr_end_fl + sw a0, 0(a0) + + /* Restore a0 - a7, t0 - t6, ra */ + lw a0, 0(sp) + lw a1, 4(sp) + lw a2, 8(sp) + lw a3, 12(sp) + lw a4, 16(sp) + lw a5, 20(sp) + lw a6, 24(sp) + lw a7, 28(sp) + lw t0, 32(sp) + lw t1, 36(sp) + lw t2, 40(sp) + lw t3, 44(sp) + lw t4, 48(sp) + lw t5, 52(sp) + lw t6, 56(sp) + lw ra, 60(sp) + addi sp, sp, (CONTEXT_SIZE) + + mret diff --git a/components/esp_system/port/arch/riscv/esp_ipc_isr_port.c b/components/esp_system/port/arch/riscv/esp_ipc_isr_port.c new file mode 100644 index 0000000000..181d6075f8 --- /dev/null +++ b/components/esp_system/port/arch/riscv/esp_ipc_isr_port.c @@ -0,0 +1,48 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/soc.h" +#include "soc/interrupts.h" +#include "soc/hp_system_reg.h" +#include "esp_intr_alloc.h" +#include "riscv/interrupt.h" +#include "esp_rom_sys.h" +#include "esp_cpu.h" +#include "esp_attr.h" +#include "sdkconfig.h" + + +void esp_ipc_isr_port_init(const int cpuid) +{ + uint32_t intr_source = ETS_FROM_CPU_INTR2_SOURCE + cpuid; // ETS_FROM_CPU_INTR2_SOURCE and ETS_FROM_CPU_INTR3_SOURCE + + esp_intr_disable_source(ETS_IPC_ISR_INUM); + + esp_rom_route_intr_matrix(cpuid, intr_source, ETS_IPC_ISR_INUM); + + esp_cpu_intr_set_type(ETS_IPC_ISR_INUM, INTR_TYPE_LEVEL); + +#if CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 + esp_cpu_intr_set_priority(ETS_IPC_ISR_INUM, 5); +#elif CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4 + esp_cpu_intr_set_priority(ETS_IPC_ISR_INUM, 4); +#else + #error "CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL is not defined!" +#endif + + esp_intr_enable_source(ETS_IPC_ISR_INUM); +} + +IRAM_ATTR void esp_ipc_isr_port_int_trigger(const int cpuid) +{ + if (cpuid == 0) { + // it runs an interrupt on cpu0 + REG_WRITE(HP_SYSTEM_CPU_INT_FROM_CPU_2_REG, HP_SYSTEM_CPU_INT_FROM_CPU_2); + } else { + // it runs an interrupt on cpu1 + REG_WRITE(HP_SYSTEM_CPU_INT_FROM_CPU_3_REG, HP_SYSTEM_CPU_INT_FROM_CPU_3); + } +} diff --git a/components/esp_system/port/arch/riscv/esp_ipc_isr_routines.c b/components/esp_system/port/arch/riscv/esp_ipc_isr_routines.c new file mode 100644 index 0000000000..27ea167f3d --- /dev/null +++ b/components/esp_system/port/arch/riscv/esp_ipc_isr_routines.c @@ -0,0 +1,12 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "stdint.h" + +void esp_ipc_isr_waiting_for_finish_cmd(void* ipc_isr_finish_cmd) +{ + while (*(volatile uint32_t*)ipc_isr_finish_cmd == 0) { }; +} diff --git a/components/esp_system/port/arch/xtensa/esp_ipc_isr_port.c b/components/esp_system/port/arch/xtensa/esp_ipc_isr_port.c new file mode 100644 index 0000000000..7224c38ee9 --- /dev/null +++ b/components/esp_system/port/arch/xtensa/esp_ipc_isr_port.c @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include "soc/soc.h" +#include "soc/interrupts.h" +#include "soc/dport_reg.h" +#ifndef CONFIG_IDF_TARGET_ESP32 +#include "soc/system_reg.h" +#endif +#include "esp_rom_sys.h" +#include "esp_intr_alloc.h" +#include "esp_attr.h" +#include "sdkconfig.h" + + +void esp_ipc_isr_port_init(const int cpuid) +{ + 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); + esp_rom_route_intr_matrix(cpuid, intr_source, ETS_IPC_ISR_INUM); + ESP_INTR_ENABLE(ETS_IPC_ISR_INUM); +} + +IRAM_ATTR void esp_ipc_isr_port_int_trigger(const int cpuid) +{ + if (cpuid == 0) { + // it runs an interrupt on cpu0 + DPORT_REG_WRITE(SYSTEM_CPU_INTR_FROM_CPU_2_REG, SYSTEM_CPU_INTR_FROM_CPU_2); + } else { + // it runs an interrupt on cpu1 + DPORT_REG_WRITE(SYSTEM_CPU_INTR_FROM_CPU_3_REG, SYSTEM_CPU_INTR_FROM_CPU_3); + } +} diff --git a/components/esp_system/port/arch/xtensa/esp_ipc_isr.c b/components/esp_system/port/esp_ipc_isr.c similarity index 85% rename from components/esp_system/port/arch/xtensa/esp_ipc_isr.c rename to components/esp_system/port/esp_ipc_isr.c index 5ffcb76e21..0e7c1cdbc7 100644 --- a/components/esp_system/port/arch/xtensa/esp_ipc_isr.c +++ b/components/esp_system/port/esp_ipc_isr.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2017-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2017-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -10,19 +10,12 @@ #include #include "esp_err.h" #include "esp_attr.h" -#include "soc/soc.h" -#include "soc/dport_reg.h" -#ifndef CONFIG_IDF_TARGET_ESP32 -#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_private/esp_ipc_isr.h" +#include "esp_private/esp_ipc_isr_port.h" #include "esp_ipc_isr.h" -#include "xtensa/core-macros.h" #include "sdkconfig.h" static portMUX_TYPE s_ipc_isr_mux = portMUX_INITIALIZER_UNLOCKED; @@ -61,10 +54,7 @@ static void esp_ipc_isr_call_and_wait(esp_ipc_isr_func_t func, void* arg, esp_ip void esp_ipc_isr_init(void) { 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); - esp_rom_route_intr_matrix(cpuid, intr_source, ETS_IPC_ISR_INUM); - ESP_INTR_ENABLE(ETS_IPC_ISR_INUM); + esp_ipc_isr_port_init(cpuid); if (cpuid != 0) { s_stall_state = STALL_STATE_RUNNING; @@ -76,14 +66,14 @@ void esp_ipc_isr_init(void) /* Public API functions */ -void IRAM_ATTR esp_ipc_isr_asm_call(esp_ipc_isr_func_t func, void* arg) +void IRAM_ATTR esp_ipc_isr_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) +void IRAM_ATTR esp_ipc_isr_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); @@ -201,14 +191,8 @@ static void IRAM_ATTR esp_ipc_isr_call_and_wait(esp_ipc_isr_func_t func, void* a 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); - } - + // Trigger an interrupt on the opposite core. + esp_ipc_isr_port_int_trigger(!cpu_id); // IPC_ISR handler will be called and `...isr_start` and `...isr_end` will be updated there if (wait_for == IPC_ISR_WAIT_FOR_START) { diff --git a/components/esp_system/port/include/private/esp_private/esp_ipc_isr_port.h b/components/esp_system/port/include/private/esp_private/esp_ipc_isr_port.h new file mode 100644 index 0000000000..fc7ad95d56 --- /dev/null +++ b/components/esp_system/port/include/private/esp_private/esp_ipc_isr_port.h @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: 2023 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 + +/** + * @brief Initialize the IPC ISR interrupt for a specific CPU. + * + * This function initializes the IPC ISR (Inter-Processor Communication Interrupt) + * for a specific CPU core. It configures the interrupt source and enables the + * IPC ISR interrupt for the specified CPU. + * + * @param[in] cpuid The ID of the CPU core to initialize IPC ISR for. + */ +void esp_ipc_isr_port_init(const int cpuid); + +/** + * @brief Trigger an interrupt on a specific CPU core. + * + * @param[in] cpuid The ID of the CPU core to trigger the interrupt on (0 or 1). + */ +void esp_ipc_isr_port_int_trigger(const int cpuid); + +#endif // CONFIG_ESP_IPC_ISR_ENABLE + +#ifdef __cplusplus +} +#endif diff --git a/components/esp_system/test_apps/esp_system_unity_tests/main/CMakeLists.txt b/components/esp_system/test_apps/esp_system_unity_tests/main/CMakeLists.txt index 46b79fa772..3e822ddec1 100644 --- a/components/esp_system/test_apps/esp_system_unity_tests/main/CMakeLists.txt +++ b/components/esp_system/test_apps/esp_system_unity_tests/main/CMakeLists.txt @@ -16,7 +16,12 @@ set(SRC "test_app_main.c" "test_task_wdt.c") if(CONFIG_ESP_IPC_ISR_ENABLE) - list(APPEND SRC "test_ipc_isr.c" "test_ipc_isr.S") + list(APPEND SRC "test_ipc_isr.c") + if(CONFIG_IDF_TARGET_ARCH_XTENSA) + list(APPEND SRC "port/arch/xtensa/test_ipc_isr.S") + elseif(CONFIG_IDF_TARGET_ARCH_RISCV) + list(APPEND SRC "port/arch/riscv/test_ipc_isr.c") + endif() endif() idf_component_register(SRCS ${SRC} diff --git a/components/esp_system/test_apps/esp_system_unity_tests/main/port/arch/riscv/test_ipc_isr.c b/components/esp_system/test_apps/esp_system_unity_tests/main/port/arch/riscv/test_ipc_isr.c new file mode 100644 index 0000000000..7ef0bbbdca --- /dev/null +++ b/components/esp_system/test_apps/esp_system_unity_tests/main/port/arch/riscv/test_ipc_isr.c @@ -0,0 +1,29 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "sdkconfig.h" + +#if CONFIG_ESP_IPC_ISR_ENABLE + +void esp_test_ipc_isr_callback(void *arg) { + uint32_t value = 0xa5a5; + *(volatile uint32_t*)arg = value; +} + +void esp_test_ipc_isr_get_other_core_id(void *arg) { + uint32_t core_id; + __asm volatile("csrr %0, mhartid" : "=r"(core_id)); + *(volatile uint32_t*)arg = core_id; +} + +void esp_test_ipc_isr_get_cycle_count_other_cpu(void *arg) { + uint32_t cycle_count; + __asm volatile("rdcycle %0;" : "=r"(cycle_count)); + *(volatile uint32_t*)arg = cycle_count; +} + +#endif // CONFIG_ESP_IPC_ISR_ENABLE diff --git a/components/esp_system/test_apps/esp_system_unity_tests/main/test_ipc_isr.S b/components/esp_system/test_apps/esp_system_unity_tests/main/port/arch/xtensa/test_ipc_isr.S similarity index 86% rename from components/esp_system/test_apps/esp_system_unity_tests/main/test_ipc_isr.S rename to components/esp_system/test_apps/esp_system_unity_tests/main/port/arch/xtensa/test_ipc_isr.S index b19a51c202..700d0290f4 100644 --- a/components/esp_system/test_apps/esp_system_unity_tests/main/test_ipc_isr.S +++ b/components/esp_system/test_apps/esp_system_unity_tests/main/port/arch/xtensa/test_ipc_isr.S @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -13,18 +13,18 @@ #include #include -/* esp_test_ipc_isr_asm(void *arg) +/* esp_test_ipc_isr_callback(void *arg) * * It should be called by the CALLX0 command from the handler of High-priority interrupt. * 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 + .global esp_test_ipc_isr_callback + .type esp_test_ipc_isr_callback, @function // Args: // a2 - void* arg -esp_test_ipc_isr_asm: +esp_test_ipc_isr_callback: movi a3, 0xa5a5 s32i a3, a2, 0 ret diff --git a/components/esp_system/test_apps/esp_system_unity_tests/main/test_ipc_isr.c b/components/esp_system/test_apps/esp_system_unity_tests/main/test_ipc_isr.c index c44ff58174..a17fade9ce 100644 --- a/components/esp_system/test_apps/esp_system_unity_tests/main/test_ipc_isr.c +++ b/components/esp_system/test_apps/esp_system_unity_tests/main/test_ipc_isr.c @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -17,12 +17,18 @@ #ifdef CONFIG_ESP_IPC_ISR_ENABLE -void esp_test_ipc_isr_asm(void* arg); +#if CONFIG_IDF_TARGET_ARCH_RISCV + #define STORE_ERROR "Store access fault" +#else + #define STORE_ERROR "StoreProhibited" +#endif + +void esp_test_ipc_isr_callback(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); + esp_ipc_isr_call_blocking(esp_test_ipc_isr_callback, &val); TEST_ASSERT_EQUAL_HEX(val, 0xa5a5); } @@ -32,13 +38,13 @@ 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); + esp_ipc_isr_call_blocking(esp_test_ipc_isr_get_other_core_id, &val); TEST_ASSERT_EQUAL_HEX(val, 1); } -static void do_esp_ipc_isr_asm_call_blocking(void) +static void do_esp_ipc_isr_call_blocking(void) { - esp_ipc_isr_asm_call_blocking(esp_test_ipc_isr_asm, NULL); + esp_ipc_isr_call_blocking(esp_test_ipc_isr_callback, NULL); } static void check_reset_reason_panic(void) @@ -46,8 +52,8 @@ static void check_reset_reason_panic(void) TEST_ASSERT_EQUAL(ESP_RST_PANIC, esp_reset_reason()); } -TEST_CASE_MULTIPLE_STAGES("Test ipc_isr exception in asm func leads to StoreProhibited not to Unhandled debug exception", "[ipc][reset=StoreProhibited,SW_CPU_RESET]", - do_esp_ipc_isr_asm_call_blocking, +TEST_CASE_MULTIPLE_STAGES("Test ipc_isr exception in asm func leads to StoreProhibited not to Unhandled debug exception", "[ipc][reset="STORE_ERROR",SW_CPU_RESET]", + do_esp_ipc_isr_call_blocking, check_reset_reason_panic) void esp_test_ipc_isr_get_cycle_count_other_cpu(void* arg); @@ -55,7 +61,7 @@ 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_ipc_isr_call_blocking(esp_test_ipc_isr_get_cycle_count_other_cpu, &val); esp_rom_printf("CCOUNT CPU0 = %d\n", esp_cpu_get_cycle_count()); esp_rom_printf("CCOUNT CPU1 = %d\n", val); } @@ -70,12 +76,12 @@ static void task_asm(void *arg) printf("task_asm\n"); while (s_stop == false) { val = 0x5a5a; - esp_ipc_isr_asm_call_blocking(esp_test_ipc_isr_asm, &val); + esp_ipc_isr_call_blocking(esp_test_ipc_isr_callback, &val); TEST_ASSERT_EQUAL_HEX(val, 0xa5a5); ++counter; } printf("task_asm counter = %d\n", counter); - TEST_ASSERT_GREATER_THAN(10000, counter); + TEST_ASSERT_GREATER_THAN(1000, counter); xSemaphoreGive(*sema); vTaskDelete(NULL); } diff --git a/components/riscv/vectors_clic.S b/components/riscv/vectors_clic.S index 2531a9e545..e1b2ad0e35 100644 --- a/components/riscv/vectors_clic.S +++ b/components/riscv/vectors_clic.S @@ -14,6 +14,12 @@ #define MEMPROT_ISR _interrupt_handler #endif // CONFIG_ESP_SYSTEM_MEMPROT_FEATURE +#if CONFIG_ESP_IPC_ISR_ENABLE + #define IPC_ISR_HANDLER esp_ipc_isr_handler +#else + #define IPC_ISR_HANDLER _interrupt_handler +#endif // CONFIG_ESP_IPC_ISR_ENABLE + /* The system interrupts are not used for now, so trigger a panic every time one occurs. */ #define _system_int_handler _panic_handler @@ -104,7 +110,7 @@ _mtvt_table: .word _panic_handler /* 41: ETS_CACHEERR_INUM (+16) panic-interrupt (soc-level panic) */ .word MEMPROT_ISR /* 42: ETS_MEMPROT_ERR_INUM (+16) handler (soc-level panic) */ .word _interrupt_handler /* 43: Free interrupt number */ - .word _interrupt_handler /* 44: Free interrupt number */ + .word IPC_ISR_HANDLER /* 44: ETS_IPC_ISR_INUM (+16) handler*/ .word _interrupt_handler /* 45: Free interrupt number */ .word _interrupt_handler /* 46: Free interrupt number */ .word _interrupt_handler /* 47: Free interrupt number */ diff --git a/components/soc/esp32p4/include/soc/hp_system_reg.h b/components/soc/esp32p4/include/soc/hp_system_reg.h index e44418cfbc..ec9b55a793 100644 --- a/components/soc/esp32p4/include/soc/hp_system_reg.h +++ b/components/soc/esp32p4/include/soc/hp_system_reg.h @@ -5,7 +5,6 @@ */ #pragma once -#include #include "soc/soc.h" #ifdef __cplusplus extern "C" { diff --git a/components/soc/esp32p4/include/soc/soc.h b/components/soc/esp32p4/include/soc/soc.h index ba2c157b2c..735a0b7a59 100644 --- a/components/soc/esp32p4/include/soc/soc.h +++ b/components/soc/esp32p4/include/soc/soc.h @@ -234,6 +234,7 @@ #define ETS_T1_WDT_INUM 24 #define ETS_CACHEERR_INUM 25 #define ETS_MEMPROT_ERR_INUM 26 +#define ETS_IPC_ISR_INUM 28 //CPU0 Max valid interrupt number #define ETS_MAX_INUM 31 diff --git a/docs/docs_not_updated/esp32p4.txt b/docs/docs_not_updated/esp32p4.txt index 1080e580b8..b9efff8743 100644 --- a/docs/docs_not_updated/esp32p4.txt +++ b/docs/docs_not_updated/esp32p4.txt @@ -183,7 +183,6 @@ api-reference/system/bootloader_image_format.rst api-reference/system/inc/power_management_esp32p4.rst api-reference/system/heap_debug.rst api-reference/system/mm.rst -api-reference/system/ipc.rst api-reference/system/esp_https_ota.rst api-reference/system/ulp-risc-v.rst api-reference/system/esp_err.rst diff --git a/docs/en/api-reference/system/index.rst b/docs/en/api-reference/system/index.rst index 99d3457e2d..24e2bf0ad2 100644 --- a/docs/en/api-reference/system/index.rst +++ b/docs/en/api-reference/system/index.rst @@ -24,7 +24,7 @@ System API heap_debug esp_timer internal-unstable - :not CONFIG_FREERTOS_UNICORE or esp32p4: ipc + :not CONFIG_FREERTOS_UNICORE: ipc intr_alloc log misc_system_api diff --git a/docs/en/api-reference/system/ipc.rst b/docs/en/api-reference/system/ipc.rst index 6fea3b2816..35099e2ac4 100644 --- a/docs/en/api-reference/system/ipc.rst +++ b/docs/en/api-reference/system/ipc.rst @@ -14,11 +14,7 @@ Due to the dual core nature of the {IDF_TARGET_NAME}, there are instances where - On particular chips (such as the ESP32), accessing memory that is exclusive to a particular CPU (such as RTC Fast Memory). - Reading the registers/state of another CPU. - -.. only:: not esp32p4 - - The IPC (Inter-Processor Call) feature allows a particular CPU (the calling CPU) to trigger the execution of a callback function on another CPU (the target CPU). The IPC feature allows execution of a callback function on the target CPU in either a task context, or a High Priority Interrupt context (see :doc:`/api-guides/hlinterrupts` for more details). Depending on the context that the callback function is executed in, different restrictions apply to the implementation of the callback function. - +The IPC (Inter-Processor Call) feature allows a particular CPU (the calling CPU) to trigger the execution of a callback function on another CPU (the target CPU). The IPC feature allows execution of a callback function on the target CPU in either ``a task context``, or ``an interrupt context``. Depending on the context that the callback function is executed in, different restrictions apply to the implementation of the callback function. IPC in Task Context ------------------- @@ -46,76 +42,103 @@ The IPC feature offers the API listed below to execute a callback in a task cont - :cpp:func:`esp_ipc_call` triggers an IPC call on the target CPU. This function will block until the target CPU's IPC task **begins** execution of the callback. - :cpp:func:`esp_ipc_call_blocking` triggers an IPC on the target CPU. This function will block until the target CPU's IPC task **completes** execution of the callback. -.. only:: not esp32p4 +IPC in Interrupt Context +------------------------ - IPC in ISR Context - ------------------ +In some cases, we need to quickly obtain the state of another CPU such as in a core dump, GDB stub, various unit tests, and DPORT workaround. The IPC ISR feature implements the High Priority Interrupt context by reserving a High Priority Interrupt on each CPU for IPC usage. When a calling CPU needs to execute a callback on the target CPU, the callback will execute in the context of the High Priority Interrupt of the target CPU. - In some cases, we need to quickly obtain the state of another CPU such as in a core dump, GDB stub, various unit tests, and DPORT workaround. For such scenarios, the IPC feature supports execution of callbacks in a :doc:`High Priority Interrupt ` context. The IPC feature implements the High Priority Interrupt context by reserving a High Priority Interrupt on each CPU for IPC usage. When a calling CPU needs to execute a callback on the target CPU, the callback will execute in the context of the High Priority Interrupt of the target CPU. +.. only:: CONFIG_IDF_TARGET_ARCH_XTENSA - When using IPCs in High Priority Interrupt context, users need to consider the following: + For such scenarios, the IPC ISR feature supports execution of callbacks in a :doc:`High Priority Interrupt ` context. - - Since the callback is executed in a High Priority Interrupt context, the callback must be written entirely in assembly. See the API Usage below for more details regarding writing assembly callbacks. - - The priority of the reserved High Priority Interrupt is dependent on the :ref:`CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL` option - - When the callback executes: +When using IPCs in High Priority Interrupt context, users need to consider the following: - - The calling CPU will disable interrupts of level 3 and lower - - Although the priority of the reserved interrupt depends on :ref:`CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL`, during the execution IPC ISR callback, the target CPU will disable interrupts of level 5 and lower regardless of what :ref:`CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL` is set to. +.. list:: + + :CONFIG_IDF_TARGET_ARCH_XTENSA: - Since the callback is executed in a High Priority Interrupt context, the callback must be written entirely in assembly. See the API Usage below for more details regarding writing assembly callbacks. + - The priority of the reserved High Priority Interrupt is dependent on the :ref:`CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL` option. + +When the callback executes, users need to consider the following: + +.. list:: + + - The calling CPU will disable interrupts of level 3 and lower. + :CONFIG_IDF_TARGET_ARCH_XTENSA: - Although the priority of the reserved interrupt depends on :ref:`CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL`, during the execution IPC ISR callback, the target CPU will disable interrupts of level 5 and lower regardless of what :ref:`CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL` is set to. + :CONFIG_IDF_TARGET_ARCH_RISCV: - Although the priority of the reserved interrupt depends on :ref:`CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL`, during the execution IPC ISR callback, the target CPU will disable all interrupts. API Usage ^^^^^^^^^ -High Priority Interrupt IPC callbacks have the following restrictions: +.. only:: CONFIG_IDF_TARGET_ARCH_XTENSA -- The callback must be of type ``void func(void *arg)`` but implemented entirely in assembly -- The callback is invoked via the ``CALLX0`` instruction with register windowing disabled, thus the callback: - - Must not call any register window related instructions (e.g., ``entry`` and ``retw``). - - Must not call other C functions as register windowing is disabled -- The callback should be placed in IRAM at a 4-byte aligned address -- (On invocation of/after returning from) the callback, the registers ``a2, a3, a4`` are (saved/restored) automatically thus can be used in the callback. The callback should **ONLY** use those registers. - - ``a2`` contains the ``void *arg`` of the callback - - ``a3/a4`` are free to use as scratch registers + High Priority Interrupt IPC callbacks have the following restrictions: + + - The callback must be of type ``void func(void *arg)`` but implemented entirely in assembly + - The callback is invoked via the ``CALLX0`` instruction with register windowing disabled, thus the callback: + - Must not call any register window related instructions (e.g., ``entry`` and ``retw``). + - Must not call other C functions as register windowing is disabled + - The callback should be placed in IRAM at a 4-byte aligned address + - (On invocation of/after returning from) the callback, the registers ``a2, a3, a4`` are (saved/restored) automatically thus can be used in the callback. The callback should **ONLY** use those registers. + - ``a2`` contains the ``void *arg`` of the callback + - ``a3/a4`` are free to use as scratch registers + +.. only:: CONFIG_IDF_TARGET_ARCH_RISCV + + High Priority Interrupt IPC callbacks have the same restrictions as for regular interrupt handlers. The callback function can be written in C. The IPC feature offers the API listed below to execute a callback in a High Priority Interrupt context. -- :cpp:func:`esp_ipc_isr_asm_call` triggers an IPC call on the target CPU. This function will busy-wait until the target CPU begins execution of the callback. -- :cpp:func:`esp_ipc_isr_asm_call_blocking` triggers an IPC call on the target CPU. This function will busy-wait until the target CPU completes execution of the callback. +- :cpp:func:`esp_ipc_isr_call` triggers an IPC call on the target CPU. This function will busy-wait until the target CPU begins execution of the callback. +- :cpp:func:`esp_ipc_isr_call_blocking` triggers an IPC call on the target CPU. This function will busy-wait until the target CPU completes execution of the callback. -The following code-blocks demonstrates a High Priority Interrupt IPC callback written in assembly that simply reads the target CPU's cycle count. +.. only:: CONFIG_IDF_TARGET_ARCH_XTENSA -.. code-block:: asm + The following code-blocks demonstrates a High Priority Interrupt IPC callback written in assembly that simply reads the target CPU's cycle count. - /* esp_test_ipc_isr_get_cycle_count_other_cpu(void *arg) */ - // this function reads CCOUNT of the target CPU and stores it in 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 + .. code-block:: asm -.. code-block:: c + /* esp_test_ipc_isr_get_cycle_count_other_cpu(void *arg) */ + // this function reads CCOUNT of the target CPU and stores it in 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 - unit32_t cycle_count; - esp_ipc_isr_asm_call_blocking(esp_test_ipc_isr_get_cycle_count_other_cpu, (void *)cycle_count); + .. code-block:: c -.. note:: + unit32_t cycle_count; + esp_ipc_isr_call_blocking(esp_test_ipc_isr_get_cycle_count_other_cpu, (void *)cycle_count); - The number of scratch registers available for use is sufficient for most simple use cases. But if your callback requires more scratch registers, ``void *arg`` can point to a buffer that is used as a register save area. The callback can then save and restore more registers. See the :example:`system/ipc/ipc_isr`. + .. note:: -.. note:: + The number of scratch registers available for use is sufficient for most simple use cases. But if your callback requires more scratch registers, ``void *arg`` can point to a buffer that is used as a register save area. The callback can then save and restore more registers. See the :example:`system/ipc/ipc_isr`. - For more examples of High Priority Interrupt IPC callbacks, see :idf_file:`components/esp_system/port/arch/xtensa/esp_ipc_isr_routines.S` and :`components/esp_system/test/test_ipc_isr.S` + .. note:: + + For more examples of High Priority Interrupt IPC callbacks, see :idf_file:`components/esp_system/port/arch/xtensa/esp_ipc_isr_routines.S` and :idf_file:`components/esp_system/test_apps/esp_system_unity_tests/main/port/arch/xtensa/test_ipc_isr.S`. + +.. only:: CONFIG_IDF_TARGET_ARCH_RISCV + + See :idf_file:`examples/system/ipc/ipc_isr/riscv/main/main.c` for an example of its use. + +.. only:: CONFIG_IDF_TARGET_ARCH_XTENSA + + See :idf_file:`examples/system/ipc/ipc_isr/xtensa/main/main.c` for an example of its use. The High Priority Interrupt IPC API also provides the following convenience functions that can stall/resume the target CPU. These API utilize the High Priority Interrupt IPC, but supply their own internal callbacks: -- :cpp:func:`esp_ipc_isr_stall_other_cpu` stalls the target CPU. The calling CPU disables interrupts of level 3 and lower while the target CPU will busy-wait with interrupts of level 5 and lower disabled. The target CPU will busy-wait until :cpp:func:`esp_ipc_isr_release_other_cpu` is called. -- :cpp:func:`esp_ipc_isr_release_other_cpu` resumes the target CPU. +.. list:: + + :CONFIG_IDF_TARGET_ARCH_RISCV: - :cpp:func:`esp_ipc_isr_stall_other_cpu` stalls the target CPU. The calling CPU disables interrupts of level 3 and lower while the target CPU will busy-wait with all interrupts disabled. The target CPU will busy-wait until :cpp:func:`esp_ipc_isr_release_other_cpu` is called. + :CONFIG_IDF_TARGET_ARCH_XTENSA: - :cpp:func:`esp_ipc_isr_stall_other_cpu` stalls the target CPU. The calling CPU disables interrupts of level 3 and lower while the target CPU will busy-wait with interrupts of level 5 and lower disabled. The target CPU will busy-wait until :cpp:func:`esp_ipc_isr_release_other_cpu` is called. + - :cpp:func:`esp_ipc_isr_release_other_cpu` resumes the target CPU. API Reference ------------- diff --git a/examples/system/.build-test-rules.yml b/examples/system/.build-test-rules.yml index 22712a0391..8d4f8c4a09 100644 --- a/examples/system/.build-test-rules.yml +++ b/examples/system/.build-test-rules.yml @@ -80,11 +80,21 @@ examples/system/himem: temporary: true reason: the other targets are not tested yet -examples/system/ipc/ipc_isr: +examples/system/ipc/ipc_isr/riscv: enable: - - if: IDF_TARGET == "esp32" or IDF_TARGET == "esp32s3" + - if: IDF_TARGET_ARCH_RISCV == 1 and ESP_IPC_ISR_ENABLE == 1 temporary: true - reason: the other targets are not tested yet + reason: The test is intended only for multi-core chips + disable_test: + - if: IDF_TARGET == "esp32p4" + temporary: true + reason: lack of runners + +examples/system/ipc/ipc_isr/xtensa: + enable: + - if: IDF_TARGET_ARCH_XTENSA == 1 and ESP_IPC_ISR_ENABLE == 1 + temporary: true + reason: The test is intended only for multi-core chips examples/system/light_sleep: disable: diff --git a/examples/system/ipc/ipc_isr/CMakeLists.txt b/examples/system/ipc/ipc_isr/riscv/CMakeLists.txt similarity index 100% rename from examples/system/ipc/ipc_isr/CMakeLists.txt rename to examples/system/ipc/ipc_isr/riscv/CMakeLists.txt diff --git a/examples/system/ipc/ipc_isr/riscv/README.md b/examples/system/ipc/ipc_isr/riscv/README.md new file mode 100644 index 0000000000..df83241e9c --- /dev/null +++ b/examples/system/ipc/ipc_isr/riscv/README.md @@ -0,0 +1,56 @@ +| Supported Targets | ESP32P4 | +| ----------------- | ------- | + +# IPC ISR Example + +This example demonstrates how to use the IPC ISR feature (which allows an IPC to run in the context of a High Priority Interrupt). The level of the IPC ISR interrupt depends on the `CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL` option. The IPC ISR feature can be useful in cases where users need to quickly get the state of the other CPU (consult the [IPC documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/ipc.html)). Unlike ESP32 or ESP32S3, for this chip, the callback function can be written in C. + +The first callback `get_mstatus_other_cpu()` demonstrates a callback that simply returns the `MSTATUS` register of other core. + +The second callback `extended_ipc_isr_func()` demonstrates how to return multiple values from the callback. The callback's `void *arg` points to a structure that contains the following: + - `uint32_t in[];` that gives the callback multiple input arguments + - `uint32_t out[];` that gives the callback multiple output arguments + +The `extended_ipc_isr_func()` callback uses the `in[]` arguments does some work and then writes to `out[]`. + +## How to use example + +### Hardware Required + +Example should be able to run on any commonly available ESP32P4 development board. The chip should have two cores. + +### Configure the project + +- `CONFIG_FREERTOS_UNICORE` - disabled, +- `CONFIG_ESP_IPC_ISR_ENABLE` - enabled. + +``` +idf.py menuconfig +``` + +### Build and Flash + +``` +idf.py build flash monitor +``` + +(To exit the serial monitor, type ``Ctrl-]``.) + +See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects. + +## Example output + +``` +I (411) example: Start +I (421) example: call get_mstatus_other_cpu +I (421) example: MSTATUS = 0x3880 +I (421) example: call extended_ipc_isr_func +I (431) example: in[0] = 0x1 +I (431) example: in[1] = 0x2 +I (441) example: in[2] = 0x3 +I (441) example: out[0] = (in[0] | in[1] | in[2]) = 0x3 +I (451) example: out[1] = (in[0] + in[1] + in[2]) = 0x6 +I (451) example: out[2] = MCAUSE of other cpu = 0xb800002c +I (461) example: out[3] = MSTATUS of other cpu = 0x3880 +I (461) example: End +``` diff --git a/examples/system/ipc/ipc_isr/riscv/main/CMakeLists.txt b/examples/system/ipc/ipc_isr/riscv/main/CMakeLists.txt new file mode 100644 index 0000000000..8f8999c1ab --- /dev/null +++ b/examples/system/ipc/ipc_isr/riscv/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "main.c" + "callbacks.c" + INCLUDE_DIRS ".") diff --git a/examples/system/ipc/ipc_isr/riscv/main/callbacks.c b/examples/system/ipc/ipc_isr/riscv/main/callbacks.c new file mode 100644 index 0000000000..91a7286c0d --- /dev/null +++ b/examples/system/ipc/ipc_isr/riscv/main/callbacks.c @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "callbacks.h" + +void get_mstatus_other_cpu(void *arg) { + uint32_t mstatus_value; + asm volatile ("csrr %0, mstatus" : "=r" (mstatus_value)); + *(volatile uint32_t*)arg = mstatus_value; +} + +void extended_ipc_isr_func(void* arg) { + arg_data_t *a = (arg_data_t *)arg; + a->out[0] = a->in[0] | a->in[1] | a->in[2]; + a->out[1] = a->in[0] + a->in[1] + a->in[2]; + asm volatile ("csrr %0, mcause" : "=r" (a->out[2])); + asm volatile ("csrr %0, mstatus" : "=r" (a->out[3])); +} diff --git a/examples/system/ipc/ipc_isr/riscv/main/callbacks.h b/examples/system/ipc/ipc_isr/riscv/main/callbacks.h new file mode 100644 index 0000000000..207fa7b9f4 --- /dev/null +++ b/examples/system/ipc/ipc_isr/riscv/main/callbacks.h @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include + +typedef struct { + uint32_t in[3]; + uint32_t out[4]; +} arg_data_t; + +void get_mstatus_other_cpu(void *arg); +void extended_ipc_isr_func(void* arg); diff --git a/examples/system/ipc/ipc_isr/riscv/main/main.c b/examples/system/ipc/ipc_isr/riscv/main/main.c new file mode 100644 index 0000000000..b827efe7dc --- /dev/null +++ b/examples/system/ipc/ipc_isr/riscv/main/main.c @@ -0,0 +1,43 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include +#include +#include "esp_log.h" +#include "esp_ipc_isr.h" +#include "callbacks.h" +#include "sdkconfig.h" + +static const char* TAG = "example"; + +void app_main(void) +{ + ESP_LOGI(TAG, "Start"); + uint32_t mstatus_other_cpu = 0; + ESP_LOGI(TAG, "call get_mstatus_other_cpu"); + esp_ipc_isr_call_blocking(get_mstatus_other_cpu, &mstatus_other_cpu); + ESP_LOGI(TAG, "MSTATUS = 0x%"PRIx32, mstatus_other_cpu); + + ESP_LOGI(TAG, "call extended_ipc_isr_func"); + arg_data_t arg = { 0 }; + arg.in[0] = 0x01; + arg.in[1] = 0x02; + arg.in[2] = 0x03; + ESP_LOGI(TAG, "in[0] = 0x%"PRIx32, arg.in[0]); + ESP_LOGI(TAG, "in[1] = 0x%"PRIx32, arg.in[1]); + ESP_LOGI(TAG, "in[2] = 0x%"PRIx32, arg.in[2]); + esp_ipc_isr_call_blocking(extended_ipc_isr_func, (void*)&arg); + ESP_LOGI(TAG, "out[0] = (in[0] | in[1] | in[2]) = 0x%"PRIx32, arg.out[0]); + assert(0x03 == arg.out[0]); + ESP_LOGI(TAG, "out[1] = (in[0] + in[1] + in[2]) = 0x%"PRIx32, arg.out[1]); + assert(0x06 == arg.out[1]); + ESP_LOGI(TAG, "out[2] = MCAUSE of other cpu = 0x%"PRIx32, arg.out[2]); + assert(0xb800002c == arg.out[2]); + ESP_LOGI(TAG, "out[3] = MSTATUS of other cpu = 0x%"PRIx32, arg.out[3]); + assert(mstatus_other_cpu == arg.out[3]); + ESP_LOGI(TAG, "End"); +} diff --git a/examples/system/ipc/ipc_isr/riscv/pytest_ipc_isr_riscv.py b/examples/system/ipc/ipc_isr/riscv/pytest_ipc_isr_riscv.py new file mode 100644 index 0000000000..e0e831ce26 --- /dev/null +++ b/examples/system/ipc/ipc_isr/riscv/pytest_ipc_isr_riscv.py @@ -0,0 +1,20 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 + +import pytest +from pytest_embedded import Dut + + +@pytest.mark.esp32p4 +@pytest.mark.generic +def test_ipc_isr(dut: Dut) -> None: + dut.expect_exact('example: Start') + dut.expect_exact('example: MSTATUS = 0x3880') + dut.expect_exact('example: in[0] = 0x1') + dut.expect_exact('example: in[1] = 0x2') + dut.expect_exact('example: in[2] = 0x3') + dut.expect_exact('example: out[0] = (in[0] | in[1] | in[2]) = 0x3') + dut.expect_exact('example: out[1] = (in[0] + in[1] + in[2]) = 0x6') + dut.expect_exact('example: out[2] = MCAUSE of other cpu = 0xb800002c') + dut.expect_exact('example: out[3] = MSTATUS of other cpu = 0x3880') + dut.expect_exact('example: End') diff --git a/examples/system/ipc/ipc_isr/sdkconfig.defaults b/examples/system/ipc/ipc_isr/riscv/sdkconfig.defaults similarity index 100% rename from examples/system/ipc/ipc_isr/sdkconfig.defaults rename to examples/system/ipc/ipc_isr/riscv/sdkconfig.defaults diff --git a/examples/system/ipc/ipc_isr/xtensa/CMakeLists.txt b/examples/system/ipc/ipc_isr/xtensa/CMakeLists.txt new file mode 100644 index 0000000000..0bb2e885b9 --- /dev/null +++ b/examples/system/ipc/ipc_isr/xtensa/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's CMakeLists +# in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(ipc_isr) diff --git a/examples/system/ipc/ipc_isr/README.md b/examples/system/ipc/ipc_isr/xtensa/README.md similarity index 97% rename from examples/system/ipc/ipc_isr/README.md rename to examples/system/ipc/ipc_isr/xtensa/README.md index 0ee9ee4eb8..3edce03dd6 100644 --- a/examples/system/ipc/ipc_isr/README.md +++ b/examples/system/ipc/ipc_isr/xtensa/README.md @@ -53,7 +53,7 @@ I (324) example: in[0] = 0x1 I (334) example: in[1] = 0x2 I (334) example: in[2] = 0x3 I (334) example: out[0] = (in[0] | in[1] | in[2]) = 0x3 -I (344) example: out[1] = (in[0] & in[1] & in[2]) = 0x6 +I (344) example: out[1] = (in[0] + in[1] + in[2]) = 0x6 I (354) example: out[2] = in[2] = 0x3 I (354) example: out[3] = PS of other cpu = 0x25 I (364) example: End diff --git a/examples/system/ipc/ipc_isr/main/CMakeLists.txt b/examples/system/ipc/ipc_isr/xtensa/main/CMakeLists.txt similarity index 100% rename from examples/system/ipc/ipc_isr/main/CMakeLists.txt rename to examples/system/ipc/ipc_isr/xtensa/main/CMakeLists.txt diff --git a/examples/system/ipc/ipc_isr/main/asm_funcs.S b/examples/system/ipc/ipc_isr/xtensa/main/asm_funcs.S similarity index 100% rename from examples/system/ipc/ipc_isr/main/asm_funcs.S rename to examples/system/ipc/ipc_isr/xtensa/main/asm_funcs.S diff --git a/examples/system/ipc/ipc_isr/main/main.c b/examples/system/ipc/ipc_isr/xtensa/main/main.c similarity index 74% rename from examples/system/ipc/ipc_isr/main/main.c rename to examples/system/ipc/ipc_isr/xtensa/main/main.c index f35eb89668..353b00118d 100644 --- a/examples/system/ipc/ipc_isr/main/main.c +++ b/examples/system/ipc/ipc_isr/xtensa/main/main.c @@ -1,16 +1,12 @@ -/* 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. -*/ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ #include #include #include -#include "esp_timer.h" #include "esp_log.h" #include "esp_ipc_isr.h" #include "sdkconfig.h" @@ -36,7 +32,7 @@ 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_ipc_isr_call_blocking(get_ps_other_cpu, &ps_other_cpu); ESP_LOGI(TAG, "PS_INTLEVEL = 0x%"PRIx32, ps_other_cpu & XCHAL_PS_INTLEVEL_MASK); ESP_LOGI(TAG, "PS_EXCM = 0x%"PRIx32, (ps_other_cpu & XCHAL_PS_EXCM_MASK) >> XCHAL_PS_EXCM_SHIFT); ESP_LOGI(TAG, "PS_UM = 0x%"PRIx32, (ps_other_cpu & XCHAL_PS_UM_MASK) >> XCHAL_PS_UM_SHIFT); @@ -49,10 +45,10 @@ void app_main(void) ESP_LOGI(TAG, "in[0] = 0x%"PRIx32, arg.in[0]); ESP_LOGI(TAG, "in[1] = 0x%"PRIx32, arg.in[1]); ESP_LOGI(TAG, "in[2] = 0x%"PRIx32, arg.in[2]); - esp_ipc_isr_asm_call_blocking(extended_ipc_isr_asm, (void*)&arg); + esp_ipc_isr_call_blocking(extended_ipc_isr_asm, (void*)&arg); ESP_LOGI(TAG, "out[0] = (in[0] | in[1] | in[2]) = 0x%"PRIx32, arg.out[0]); assert(0x03 == arg.out[0]); - ESP_LOGI(TAG, "out[1] = (in[0] & in[1] & in[2]) = 0x%"PRIx32, arg.out[1]); + ESP_LOGI(TAG, "out[1] = (in[0] + in[1] + in[2]) = 0x%"PRIx32, arg.out[1]); assert(0x06 == arg.out[1]); ESP_LOGI(TAG, "out[2] = in[2] = 0x%"PRIx32, arg.out[2]); assert(0x03 == arg.out[2]); diff --git a/examples/system/ipc/ipc_isr/pytest_ipc_isr.py b/examples/system/ipc/ipc_isr/xtensa/pytest_ipc_isr_xtensa.py similarity index 92% rename from examples/system/ipc/ipc_isr/pytest_ipc_isr.py rename to examples/system/ipc/ipc_isr/xtensa/pytest_ipc_isr_xtensa.py index 72fb939b84..cc3ce4ce80 100644 --- a/examples/system/ipc/ipc_isr/pytest_ipc_isr.py +++ b/examples/system/ipc/ipc_isr/xtensa/pytest_ipc_isr_xtensa.py @@ -18,7 +18,7 @@ def test_ipc_isr(dut: Dut) -> None: dut.expect_exact('example: in[1] = 0x2') dut.expect_exact('example: in[2] = 0x3') dut.expect_exact('example: out[0] = (in[0] | in[1] | in[2]) = 0x3') - dut.expect_exact('example: out[1] = (in[0] & in[1] & in[2]) = 0x6') + dut.expect_exact('example: out[1] = (in[0] + in[1] + in[2]) = 0x6') dut.expect_exact('example: out[2] = in[2] = 0x3') dut.expect_exact('example: out[3] = PS of other cpu = 0x25') dut.expect_exact('example: End') diff --git a/examples/system/ipc/ipc_isr/xtensa/sdkconfig.defaults b/examples/system/ipc/ipc_isr/xtensa/sdkconfig.defaults new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tools/ci/check_copyright_ignore.txt b/tools/ci/check_copyright_ignore.txt index c61514a2f4..c6e23c2136 100644 --- a/tools/ci/check_copyright_ignore.txt +++ b/tools/ci/check_copyright_ignore.txt @@ -1271,7 +1271,6 @@ examples/system/gcov/main/gcov_example_main.c examples/system/gdbstub/main/gdbstub_main.c examples/system/heap_task_tracking/main/heap_task_tracking_main.c examples/system/himem/main/himem_example_main.c -examples/system/ipc/ipc_isr/main/main.c examples/system/ota/advanced_https_ota/main/advanced_https_ota_example.c examples/system/ota/native_ota_example/main/native_ota_example.c examples/system/ota/otatool/main/otatool_main.c