处理器间调用 (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 调用。在目标内核 完成 回调执行前,此函数将一直处于忙等待。
这些函数会中断另一 CPU,并在高优先级中断的上下文中执行回调。常见用法有两种:
对于不会进入与另一 CPU 共享的临界区的简单回调,可以直接调用
esp_ipc_isr_call()或esp_ipc_isr_call_blocking()。如果调用 CPU 可能进入另一 CPU 使用的临界区,或者需要在另一 CPU 保持停止时执行多个回调,则应先使用
esp_ipc_isr_stall_other_cpu()或esp_ipc_isr_stall_other_cpu_safe()暂停另一 CPU。然后使用esp_ipc_isr_call()或esp_ipc_isr_call_blocking()执行回调。操作完成后,使用esp_ipc_isr_release_other_cpu()释放另一 CPU。
请前往 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_stall_other_cpu_safe():仅当另一内核不在临界区或 ISR 上下文中时,才尝试暂停该内核。如果另一内核处于此类状态,则认为暂停不安全,会释放该内核并返回错误。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.
备注
This function can be called even if the other core is stalled (either via esp_ipc_isr_stall_other_cpu() or esp_ipc_isr_stall_other_cpu_safe()). This allows safely stalling the other CPU and executing one or more callbacks before releasing it.
- 参数:
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.
备注
This function can be called even if the other core is stalled (either via esp_ipc_isr_stall_other_cpu() or esp_ipc_isr_stall_other_cpu_safe()). This allows safely stalling the other CPU and executing one or more callbacks before releasing it.
- 参数:
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 unconditionally.
This function forces the other CPU to enter a busy-wait loop within a High Priority Interrupt context, effectively stalling its execution until esp_ipc_isr_release_other_cpu() is called.
Typical use cases include:
DPORT workaround (e.g., DPORT or APB registers) on dual-core ESP32 chips prior to v2.0.
Scenarios where the state of the other CPU does not need to be checked, stalling unconditionally.
Implementation details:
The function is initialized after FreeRTOS startup.
When invoked, it signals the other CPU to enter a high-priority interrupt and remain stalled.
If the other CPU is already in a high-priority interrupt, it is considered stalled.
The other CPU will remain stalled until esp_ipc_isr_release_other_cpu() is called.
This function does not check the current state (e.g., critical section or ISR) of the other CPU. To avoid potential deadlocks on spinlocks, use esp_ipc_isr_stall_other_cpu_safe() instead.
If the stall feature is paused via esp_ipc_isr_stall_pause(), this function has no effect.
备注
Not available in single-core mode.
备注
The caller is responsible for ensuring no deadlocks on spinlocks occur. Use esp_ipc_isr_stall_other_cpu_safe() for safer operation.
-
esp_err_t esp_ipc_isr_stall_other_cpu_safe(void)
Safely stall the other CPU.
Attempts to stall the other CPU only if it is not currently in a critical section or ISR context. If the other CPU is in a critical section or ISR, the function will return an error.
This helps to prevent potential deadlocks when both CPUs may access shared resources/spinlocks.
备注
This function is intended for scenarios where safe stalling is required and the state of the other CPU must be checked.
- 返回:
ESP_OK: The other CPU was successfully stalled.
ESP_ERR_NOT_ALLOWED: The other CPU is in a critical section or ISR context and cannot be stalled.
-
bool esp_ipc_isr_is_other_cpu_stalled(void)
Check whether the other CPU is currently stalled.
- 返回:
true if the other CPU has entered the IPC ISR stall loop, false otherwise.
-
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 previous 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().