处理器间调用 (IPC)
备注
IPC 指 处理器间调用 (Inter-Processor Call),而不是其他操作系统中所指的 进程间通信 (Inter-Process Communication)。
概述
由于 ESP32-P4 的双核特性,在某些情况下,某个回调必须在特定的内核上下文中运行。例如:
- 为特定内核的中断源分配 ISR,或释放特定内核的中断源时 
- 在特定芯片(如 ESP32)上访问某个内核独有的内存,如 RTC Fast Memory 时 
- 读取另一个内核的寄存器或状态时 
IPC 功能允许一个特定的内核(下文称“调用内核”)触发另一个内核(下文称“目标内核”)的回调函数执行,并允许目标内核在任务上下文或中断上下文中执行回调函数。在不同的上下文中,回调函数的实现限制有所不同。
任务上下文中的 IPC
在应用程序启动时,IPC 功能为每个内核创建一个 IPC 任务,从而在任务上下文中执行回调。当调用内核需要在目标内核中执行回调时,回调将在目标内核的 IPC 任务的上下文中执行。
在任务上下文中使用 IPC 功能时,需考虑以下几点:
- IPC 回调应该尽可能简短。 IPC 回调决不能阻塞或让出。 
- IPC 任务是以尽可能高的优先级创建的(即 - configMAX_PRIORITIES - 1)。- 如果启用了 CONFIG_ESP_IPC_USES_CALLERS_PRIORITY,执行回调前会降低目标内核的 IPC 任务优先级,使其等于调用内核的优先级。 
- 如果禁用了 CONFIG_ESP_IPC_USES_CALLERS_PRIORITY,目标内核将始终以尽可能高的优先级执行回调。 
 
- 如果回调较为复杂,用户可能需要通过 CONFIG_ESP_IPC_TASK_STACK_SIZE 来配置 IPC 任务的堆栈大小。 
- IPC 功能受内部互斥锁保护。因此,如果同时收到来自两个或多个调用内核的 IPC 请求,将按照“先到先得”的原则按顺序处理。 
API 用法
任务上下文中的 IPC 回调具有以下限制:
- 回调类型必须是 - esp_ipc_func_t。
- 回调 决不能阻塞或让出,以免导致目标内核的 IPC 任务阻塞或让出。 
- 回调必须避免改变 IPC 任务的任何状态,例如,不能调用 - vTaskPrioritySet(NULL, x)。
IPC 功能提供了以下 API,用于在目标内核的任务上下文中执行回调。这两个 API 允许调用内核在回调执行完成前处于阻塞,或者在回调开始执行后立即返回。
- esp_ipc_call()会在目标内核上触发一个 IPC 调用。在目标内核的 IPC 任务 开始 执行回调前,此函数会一直处于阻塞状态。
- esp_ipc_call_blocking()会在目标内核上触发一个 IPC。在目标内核的 IPC 任务 完成 回调执行前,此函数会一直处于阻塞状态。
中断上下文中的 IPC
在某些情况下,我们需要快速获取另一个内核的状态,如在核心转储、GDB Stub、各种单元测试和绕过硬件错误的过程中。IPC ISR 功能会在每个内核上保留一个高优先级中断供 IPC 使用,从而在高优先级中断上下文中执行回调。当调用内核需要在目标内核上执行回调时,该回调将在目标内核的高优先级中断的上下文中执行。
在高优先级中断上下文中使用 IPC 时,需要考虑以下几点:
- 保留的高优先级中断的优先级取决于 CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL 选项。 
当回调执行时,需考虑以下几点:
- 调用内核会禁用 3 级及以下优先级的中断。 
- 虽然保留中断的优先级取决于 CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL,但是在执行 IPC ISR 回调期间,目标内核会禁用所有的中断。 
API 用法
高优先级中断 IPC 回调函数的类型必须是 esp_ipc_isr_func_t,其限制条件与常规中断处理程序相同。回调函数可以用 C 语言编写。
IPC 功能提供了下列 API,以在高优先级中断的上下文中执行回调:
- esp_ipc_isr_call()能够在目标内核上触发一个 IPC 调用。在目标内核 开始 执行回调前,此函数将一直处于忙等待。
- esp_ipc_isr_call_blocking()能够在目标内核上触发一个 IPC 调用。在目标内核 完成 回调执行前,此函数将一直处于忙等待。
请前往 examples/system/ipc/ipc_isr/riscv/main/main.c 查看使用示例。
高优先级中断 IPC API 还提供了以下便利函数,这些函数可以暂停或恢复目标内核的执行。这些 API 利用高优先级中断 IPC,但同时提供了自己的内部回调函数:
- esp_ipc_isr_stall_other_cpu():暂停目标内核。调用内核禁用 3 级及以下级别的中断,而目标内核将在所有中断被禁用的情况下进入忙等待。在调用- esp_ipc_isr_release_other_cpu()前,目标内核会保持忙等待。
- esp_ipc_isr_release_other_cpu():恢复目标内核。
应用示例
- system/ipc/ipc_isr/riscv 演示了如何在 ESP32-P4 上使用 IPC ISR 功能在高优先级中断的上下文中运行 IPC,包括如何快速获取另一个 CPU 的状态以及如何从回调函数返回多个值。 
API 参考
Header File
- This header file can be included with: - #include "esp_ipc.h" 
Functions
- 
esp_err_t esp_ipc_call(uint32_t cpu_id, esp_ipc_func_t func, void *arg)
- Execute a callback on a given CPU. - Execute a given callback on a particular CPU. The callback must be of type "esp_ipc_func_t" and will be invoked in the context of the target CPU's IPC task. - This function will block the target CPU's IPC task has begun execution of the callback 
- If another IPC call is ongoing, this function will block until the ongoing IPC call completes 
- The stack size of the IPC task can be configured via the CONFIG_ESP_IPC_TASK_STACK_SIZE option 
 - 备注 - In single-core mode, returns ESP_ERR_INVALID_ARG for cpu_id 1. - 参数:
- cpu_id -- [in] CPU where the given function should be executed (0 or 1) 
- func -- [in] Pointer to a function of type void func(void* arg) to be executed 
- arg -- [in] Arbitrary argument of type void* to be passed into the function 
 
- 返回:
- ESP_ERR_INVALID_ARG if cpu_id is invalid 
- ESP_ERR_INVALID_STATE if the FreeRTOS scheduler is not running 
- ESP_OK otherwise 
 
 
- 
esp_err_t esp_ipc_call_blocking(uint32_t cpu_id, esp_ipc_func_t func, void *arg)
- Execute a callback on a given CPU until and block until it completes. - This function is identical to esp_ipc_call() except that this function will block until the execution of the callback completes. - 备注 - In single-core mode, returns ESP_ERR_INVALID_ARG for cpu_id 1. - 参数:
- cpu_id -- [in] CPU where the given function should be executed (0 or 1) 
- func -- [in] Pointer to a function of type void func(void* arg) to be executed 
- arg -- [in] Arbitrary argument of type void* to be passed into the function 
 
- 返回:
- ESP_ERR_INVALID_ARG if cpu_id is invalid 
- ESP_ERR_INVALID_STATE if the FreeRTOS scheduler is not running 
- ESP_OK otherwise 
 
 
Type Definitions
- 
typedef void (*esp_ipc_func_t)(void *arg)
- IPC Callback. - A callback of this type should be provided as an argument when calling esp_ipc_call() or esp_ipc_call_blocking(). 
Header File
- This header file can be included with: - #include "esp_ipc_isr.h" 
Functions
- 
void esp_ipc_isr_call(esp_ipc_isr_func_t func, void *arg)
- 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 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). 
 
 - 备注 - This function is not available in single-core mode. - 参数:
- func -- [in] Pointer to a function of type void func(void* arg) to be executed 
- arg -- [in] Arbitrary argument of type void* to be passed into the function 
 
 
- 
void esp_ipc_isr_call_blocking(esp_ipc_isr_func_t func, void *arg)
- Execute an ISR callback on the other CPU and busy-wait until it completes. - This function is identical to esp_ipc_isr_call() except that this function will busy-wait until the execution of the callback completes. - 备注 - This function is not available in single-core mode. - 参数:
- func -- [in] Pointer to a function of type void func(void* arg) to be executed 
- arg -- [in] Arbitrary argument of type void* to be passed into the function 
 
 
- 
void esp_ipc_isr_stall_other_cpu(void)
- Stall the other CPU. - This function will stall the other CPU. The other CPU is stalled by busy-waiting in the context of a High Priority Interrupt. The other CPU will not be resumed until esp_ipc_isr_release_other_cpu() is called. - This function is internally implemented using IPC ISR 
- This function is used for DPORT workaround. 
- If the stall feature is paused using esp_ipc_isr_stall_pause(), this function will have no effect 
 - 备注 - This function is not available in single-core mode. - 备注 - It is the caller's responsibility to avoid deadlocking on spinlocks 
- 
void esp_ipc_isr_release_other_cpu(void)
- Release the other CPU. - This function will release the other CPU that was previously stalled from calling esp_ipc_isr_stall_other_cpu() - This function is used for DPORT workaround. 
- If the stall feature is paused using esp_ipc_isr_stall_pause(), this function will have no effect 
 - 备注 - This function is not available in single-core mode. 
- 
void esp_ipc_isr_stall_pause(void)
- Puase the CPU stall feature. - This function will pause the CPU stall feature. Once paused, calls to esp_ipc_isr_stall_other_cpu() and esp_ipc_isr_release_other_cpu() will have no effect. If a IPC ISR call is already in progress, this function will busy-wait until the call completes before pausing the CPU stall feature. 
- 
void esp_ipc_isr_stall_abort(void)
- Abort a CPU stall. - This function will abort any stalling routine of the other CPU due to a pervious call to esp_ipc_isr_stall_other_cpu(). This function aborts the stall in a non-recoverable manner, thus should only be called in case of a panic(). - This function is used in panic handling code 
 
- 
void esp_ipc_isr_stall_resume(void)
- Resume the CPU stall feature. - This function will resume the CPU stall feature that was previously paused by calling esp_ipc_isr_stall_pause(). Once resumed, calls to esp_ipc_isr_stall_other_cpu() and esp_ipc_isr_release_other_cpu() will have effect again. 
Macros
- 
esp_ipc_isr_asm_call(func, arg)
- Execute an ISR callback on the other CPU See esp_ipc_isr_call(). 
- 
esp_ipc_isr_asm_call_blocking(func, arg)
- Execute an ISR callback on the other CPU and busy-wait until it completes See esp_ipc_isr_call_blocking(). 
Type Definitions
- 
typedef void (*esp_ipc_isr_func_t)(void *arg)
- IPC ISR Callback. - 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().