ソースを参照

feat(esp_system): Support IPC_ISR for ESP32P4

KonstantinKondrashov 2 年 前
コミット
7a878bdc50
37 ファイル変更591 行追加133 行削除
  1. 4 5
      components/esp_system/Kconfig
  2. 29 10
      components/esp_system/include/esp_ipc_isr.h
  3. 1 1
      components/esp_system/include/esp_private/esp_ipc_isr.h
  4. 10 5
      components/esp_system/port/CMakeLists.txt
  5. 78 0
      components/esp_system/port/arch/riscv/esp_ipc_isr_handler.S
  6. 48 0
      components/esp_system/port/arch/riscv/esp_ipc_isr_port.c
  7. 12 0
      components/esp_system/port/arch/riscv/esp_ipc_isr_routines.c
  8. 36 0
      components/esp_system/port/arch/xtensa/esp_ipc_isr_port.c
  9. 7 23
      components/esp_system/port/esp_ipc_isr.c
  10. 39 0
      components/esp_system/port/include/private/esp_private/esp_ipc_isr_port.h
  11. 6 1
      components/esp_system/test_apps/esp_system_unity_tests/main/CMakeLists.txt
  12. 29 0
      components/esp_system/test_apps/esp_system_unity_tests/main/port/arch/riscv/test_ipc_isr.c
  13. 5 5
      components/esp_system/test_apps/esp_system_unity_tests/main/port/arch/xtensa/test_ipc_isr.S
  14. 17 11
      components/esp_system/test_apps/esp_system_unity_tests/main/test_ipc_isr.c
  15. 7 1
      components/riscv/vectors_clic.S
  16. 0 1
      components/soc/esp32p4/include/soc/hp_system_reg.h
  17. 1 0
      components/soc/esp32p4/include/soc/soc.h
  18. 0 1
      docs/docs_not_updated/esp32p4.txt
  19. 1 1
      docs/en/api-reference/system/index.rst
  20. 73 50
      docs/en/api-reference/system/ipc.rst
  21. 13 3
      examples/system/.build-test-rules.yml
  22. 0 0
      examples/system/ipc/ipc_isr/riscv/CMakeLists.txt
  23. 56 0
      examples/system/ipc/ipc_isr/riscv/README.md
  24. 3 0
      examples/system/ipc/ipc_isr/riscv/main/CMakeLists.txt
  25. 22 0
      examples/system/ipc/ipc_isr/riscv/main/callbacks.c
  26. 15 0
      examples/system/ipc/ipc_isr/riscv/main/callbacks.h
  27. 43 0
      examples/system/ipc/ipc_isr/riscv/main/main.c
  28. 20 0
      examples/system/ipc/ipc_isr/riscv/pytest_ipc_isr_riscv.py
  29. 0 0
      examples/system/ipc/ipc_isr/riscv/sdkconfig.defaults
  30. 6 0
      examples/system/ipc/ipc_isr/xtensa/CMakeLists.txt
  31. 1 1
      examples/system/ipc/ipc_isr/xtensa/README.md
  32. 0 0
      examples/system/ipc/ipc_isr/xtensa/main/CMakeLists.txt
  33. 0 0
      examples/system/ipc/ipc_isr/xtensa/main/asm_funcs.S
  34. 8 12
      examples/system/ipc/ipc_isr/xtensa/main/main.c
  35. 1 1
      examples/system/ipc/ipc_isr/xtensa/pytest_ipc_isr_xtensa.py
  36. 0 0
      examples/system/ipc/ipc_isr/xtensa/sdkconfig.defaults
  37. 0 1
      tools/ci/check_copyright_ignore.txt

+ 4 - 5
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).

+ 29 - 10
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 </api-reference/system/ipc>` 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 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 assembly callback on the other CPU and busy-wait until it completes
+ * @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

+ 1 - 1
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);
 

+ 10 - 5
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)

+ 78 - 0
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

+ 48 - 0
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);
+    }
+}

+ 12 - 0
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) { };
+}

+ 36 - 0
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);
+    }
+}

+ 7 - 23
components/esp_system/port/arch/xtensa/esp_ipc_isr.c → 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 <assert.h>
 #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) {

+ 39 - 0
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

+ 6 - 1
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}

+ 29 - 0
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 <stdint.h>
+#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

+ 5 - 5
components/esp_system/test_apps/esp_system_unity_tests/main/test_ipc_isr.S → 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 <xtensa/config/system.h>
 #include <xtensa/hal.h>
 
-/* 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

+ 17 - 11
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);
 }

+ 7 - 1
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 */

+ 0 - 1
components/soc/esp32p4/include/soc/hp_system_reg.h

@@ -5,7 +5,6 @@
  */
 #pragma once
 
-#include <stdint.h>
 #include "soc/soc.h"
 #ifdef __cplusplus
 extern "C" {

+ 1 - 0
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
 

+ 0 - 1
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

+ 1 - 1
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

+ 73 - 50
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
+------------------------
+
+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.
+
+.. only:: CONFIG_IDF_TARGET_ARCH_XTENSA
 
-	IPC in ISR Context
-	------------------
+    For such scenarios, the IPC ISR feature supports execution of callbacks in a :doc:`High Priority Interrupt </api-guides/hlinterrupts>` 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. For such scenarios, the IPC feature supports execution of callbacks in a :doc:`High Priority Interrupt </api-guides/hlinterrupts>` 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.
+When using IPCs in High Priority Interrupt context, users need to consider the following:
 
-	When using IPCs in High Priority Interrupt context, users need to consider the following:
+.. list::
 
-	- 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:
+    :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.
 
-	    - 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.
+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
+
+    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
 
-- 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 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`.
+
+    .. 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`.
 
-    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`
+.. 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
 -------------

+ 13 - 3
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:

+ 0 - 0
examples/system/ipc/ipc_isr/CMakeLists.txt → examples/system/ipc/ipc_isr/riscv/CMakeLists.txt


+ 56 - 0
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
+```

+ 3 - 0
examples/system/ipc/ipc_isr/riscv/main/CMakeLists.txt

@@ -0,0 +1,3 @@
+idf_component_register(SRCS "main.c"
+                            "callbacks.c"
+                       INCLUDE_DIRS ".")

+ 22 - 0
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 <stdint.h>
+#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]));
+}

+ 15 - 0
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 <stdint.h>
+
+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);

+ 43 - 0
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 <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#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");
+}

+ 20 - 0
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')

+ 0 - 0
examples/system/ipc/ipc_isr/sdkconfig.defaults → examples/system/ipc/ipc_isr/riscv/sdkconfig.defaults


+ 6 - 0
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)

+ 1 - 1
examples/system/ipc/ipc_isr/README.md → 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

+ 0 - 0
examples/system/ipc/ipc_isr/main/CMakeLists.txt → examples/system/ipc/ipc_isr/xtensa/main/CMakeLists.txt


+ 0 - 0
examples/system/ipc/ipc_isr/main/asm_funcs.S → examples/system/ipc/ipc_isr/xtensa/main/asm_funcs.S


+ 8 - 12
examples/system/ipc/ipc_isr/main/main.c → 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 <stdio.h>
 #include <string.h>
 #include <inttypes.h>
-#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]);

+ 1 - 1
examples/system/ipc/ipc_isr/pytest_ipc_isr.py → 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')

+ 0 - 0
examples/system/ipc/ipc_isr/xtensa/sdkconfig.defaults


+ 0 - 1
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