Inter-Processor Call

Overview

Due to the dual core nature of the ESP32, there are instances where a certain function must be run in the context of a particular core (e.g. allocating ISR to an interrupt source of a particular core). The IPC (Inter-Processor Call) feature allows for the execution of functions on a particular CPU.

A given function can be executed on a particular core by calling esp_ipc_call() or esp_ipc_call_blocking(). IPC is implemented via two high priority FreeRTOS tasks pinned to each CPU known as the IPC Tasks. The two IPC Tasks remain inactive (blocked) until esp_ipc_call() or esp_ipc_call_blocking() is called. When an IPC Task of a particular core is unblocked, it will preempt the current running task on that core and execute a given function.

Usage

esp_ipc_call() unblocks the IPC task on a particular core to execute a given function. The task that calls esp_ipc_call() will be blocked until the IPC Task begins execution of the given function. esp_ipc_call_blocking() is similar but will block the calling task until the IPC Task has completed execution of the given function.

Functions executed by IPCs must be functions of type void func(void *arg). To run more complex functions which require a larger stack, the IPC tasks’ stack size can be configured by modifying CONFIG_ESP_IPC_TASK_STACK_SIZE in menuconfig. The IPC API is protected by a mutex hence simultaneous IPC calls are not possible.

Care should taken to avoid deadlock when writing functions to be executed by IPC, especially when attempting to take a mutex within the function.

API Reference

Functions

esp_err_t esp_ipc_call(uint32_t cpu_id, esp_ipc_func_t func, void *arg)

Execute a function on the given CPU.

Run a given function on a particular CPU. The given function must accept a void* argument and return void. The given function is run in the context of the IPC task of the CPU specified by the cpu_id parameter. The calling task will be blocked until the IPC task begins executing the given function. If another IPC call is ongoing, the calling task will block until the other IPC call completes. The stack size allocated for the IPC task can be configured in the “Inter-Processor Call (IPC) task stack size” setting in menuconfig. Increase this setting if the given function requires more stack than default.

Note

In single-core mode, returns ESP_ERR_INVALID_ARG for cpu_id 1.

Return

  • ESP_ERR_INVALID_ARG if cpu_id is invalid

  • ESP_ERR_INVALID_STATE if the FreeRTOS scheduler is not running

  • ESP_OK otherwise

Parameters
  • [in] cpu_id: CPU where the given function should be executed (0 or 1)

  • [in] func: Pointer to a function of type void func(void* arg) to be executed

  • [in] arg: Arbitrary argument of type void* to be passed into the function

esp_err_t esp_ipc_call_blocking(uint32_t cpu_id, esp_ipc_func_t func, void *arg)

Execute a function on the given CPU and blocks until it completes.

Run a given function on a particular CPU. The given function must accept a void* argument and return void. The given function is run in the context of the IPC task of the CPU specified by the cpu_id parameter. The calling task will be blocked until the IPC task completes execution of the given function. If another IPC call is ongoing, the calling task will block until the other IPC call completes. The stack size allocated for the IPC task can be configured in the “Inter-Processor Call (IPC) task stack size” setting in menuconfig. Increase this setting if the given function requires more stack than default.

Note

In single-core mode, returns ESP_ERR_INVALID_ARG for cpu_id 1.

Return

  • ESP_ERR_INVALID_ARG if cpu_id is invalid

  • ESP_ERR_INVALID_STATE if the FreeRTOS scheduler is not running

  • ESP_OK otherwise

Parameters
  • [in] cpu_id: CPU where the given function should be executed (0 or 1)

  • [in] func: Pointer to a function of type void func(void* arg) to be executed

  • [in] arg: Arbitrary argument of type void* to be passed into the function