mirror of
https://github.com/espressif/esp-idf.git
synced 2025-09-30 19:19:21 +00:00
IPC: Move ipc sources to esp_system
IPC shall be put back into esp_system as it is an 'OS additions'.
This commit is contained in:
@@ -12,6 +12,7 @@ if(BOOTLOADER_BUILD)
|
||||
idf_component_register(SRCS "${srcs}" REQUIRES spi_flash)
|
||||
else()
|
||||
list(APPEND srcs "crosscore_int.c"
|
||||
"esp_ipc.c"
|
||||
"esp_err.c"
|
||||
"freertos_hooks.c"
|
||||
"int_wdt.c"
|
||||
@@ -40,7 +41,7 @@ else()
|
||||
# should be removable once using component init functions
|
||||
# link-time registration is used.
|
||||
esp_pm app_update nvs_flash pthread app_trace esp_gdbstub
|
||||
espcoredump esp_phy efuse esp_ipc
|
||||
espcoredump esp_phy efuse
|
||||
LDFRAGMENTS "linker.lf" "app.lf")
|
||||
add_subdirectory(port)
|
||||
|
||||
|
@@ -502,3 +502,43 @@ menu "ESP System Settings"
|
||||
endchoice
|
||||
|
||||
endmenu # ESP System Settings
|
||||
|
||||
menu "IPC (Inter-Processor Call)"
|
||||
|
||||
config ESP_IPC_TASK_STACK_SIZE
|
||||
int "Inter-Processor Call (IPC) task stack size"
|
||||
range 512 65536 if !APPTRACE_ENABLE
|
||||
range 2048 65536 if APPTRACE_ENABLE
|
||||
default 2048 if APPTRACE_ENABLE
|
||||
default 1024
|
||||
help
|
||||
Configure the IPC tasks stack size. One IPC task runs on each core
|
||||
(in dual core mode), and allows for cross-core function calls.
|
||||
|
||||
See IPC documentation for more details.
|
||||
|
||||
The default stack size should be enough for most common use cases.
|
||||
It can be shrunk if you are sure that you do not use any custom
|
||||
IPC functionality.
|
||||
|
||||
config ESP_IPC_USES_CALLERS_PRIORITY
|
||||
bool "IPC runs at caller's priority"
|
||||
default y
|
||||
depends on !FREERTOS_UNICORE
|
||||
help
|
||||
If this option is not enabled then the IPC task will keep behavior
|
||||
same as prior to that of ESP-IDF v4.0, and hence IPC task will run
|
||||
at (configMAX_PRIORITIES - 1) priority.
|
||||
|
||||
config ESP_IPC_ISR_ENABLE
|
||||
bool
|
||||
default y if !FREERTOS_UNICORE
|
||||
help
|
||||
This feature serves a similar purpose to the IPC except that the callback function is run
|
||||
in the context of a level 4 interrupt (i.e., high priority/level interrupt). The IPC ISR
|
||||
feature is intended for low latency execution of simple functions written in assembly on
|
||||
another CPU. Due to being run in higher level interrupt context, the assembly functions
|
||||
should be written in a particular way (see esp_test_ipc_isr_asm() and the "High-Level Interrupts"
|
||||
chapter in hlinterrupts.rst for more details).
|
||||
|
||||
endmenu # "IPC (Inter-Processor Call)
|
||||
|
200
components/esp_system/esp_ipc.c
Normal file
200
components/esp_system/esp_ipc.c
Normal file
@@ -0,0 +1,200 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_ipc.h"
|
||||
#include "esp_ipc_isr.h"
|
||||
#include "esp_attr.h"
|
||||
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
|
||||
#if !defined(CONFIG_FREERTOS_UNICORE) || defined(CONFIG_APPTRACE_GCOV_ENABLE)
|
||||
|
||||
static TaskHandle_t s_ipc_task_handle[portNUM_PROCESSORS];
|
||||
static SemaphoreHandle_t s_ipc_mutex[portNUM_PROCESSORS]; // This mutex is used as a global lock for esp_ipc_* APIs
|
||||
static SemaphoreHandle_t s_ipc_sem[portNUM_PROCESSORS]; // Two semaphores used to wake each of ipc tasks
|
||||
static SemaphoreHandle_t s_ipc_ack[portNUM_PROCESSORS]; // Semaphore used to acknowledge that task was woken up,
|
||||
// or function has finished running
|
||||
static volatile esp_ipc_func_t s_func[portNUM_PROCESSORS]; // Function which should be called by high priority task
|
||||
static void * volatile s_func_arg[portNUM_PROCESSORS]; // Argument to pass into s_func
|
||||
typedef enum {
|
||||
IPC_WAIT_FOR_START,
|
||||
IPC_WAIT_FOR_END
|
||||
} esp_ipc_wait_t;
|
||||
|
||||
static volatile esp_ipc_wait_t s_ipc_wait[portNUM_PROCESSORS];// This variable tells high priority task when it should give
|
||||
// s_ipc_ack semaphore: before s_func is called, or
|
||||
// after it returns
|
||||
|
||||
#if CONFIG_APPTRACE_GCOV_ENABLE
|
||||
static volatile esp_ipc_func_t s_gcov_func = NULL; // Gcov dump starter function which should be called by high priority task
|
||||
static void * volatile s_gcov_func_arg; // Argument to pass into s_gcov_func
|
||||
#endif
|
||||
|
||||
static void IRAM_ATTR ipc_task(void* arg)
|
||||
{
|
||||
const int cpuid = (int) arg;
|
||||
assert(cpuid == xPortGetCoreID());
|
||||
while (true) {
|
||||
// Wait for IPC to be initiated.
|
||||
// This will be indicated by giving the semaphore corresponding to
|
||||
// this CPU.
|
||||
if (xSemaphoreTake(s_ipc_sem[cpuid], portMAX_DELAY) != pdTRUE) {
|
||||
// TODO: when can this happen?
|
||||
abort();
|
||||
}
|
||||
|
||||
#if CONFIG_APPTRACE_GCOV_ENABLE
|
||||
if (s_gcov_func) {
|
||||
(*s_gcov_func)(s_gcov_func_arg);
|
||||
s_gcov_func = NULL;
|
||||
/* we can not interfer with IPC calls so no need for further processing */
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
if (s_func[cpuid]) {
|
||||
esp_ipc_func_t func = s_func[cpuid];
|
||||
void* arg = s_func_arg[cpuid];
|
||||
|
||||
if (s_ipc_wait[cpuid] == IPC_WAIT_FOR_START) {
|
||||
xSemaphoreGive(s_ipc_ack[cpuid]);
|
||||
}
|
||||
(*func)(arg);
|
||||
if (s_ipc_wait[cpuid] == IPC_WAIT_FOR_END) {
|
||||
xSemaphoreGive(s_ipc_ack[cpuid]);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
// TODO: currently this is unreachable code. Introduce esp_ipc_uninit
|
||||
// function which will signal to both tasks that they can shut down.
|
||||
// Not critical at this point, we don't have a use case for stopping
|
||||
// IPC yet.
|
||||
// Also need to delete the semaphore here.
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
/*
|
||||
* Initialize inter-processor call module. This function is called automatically
|
||||
* on CPU start and should not be called from the application.
|
||||
*
|
||||
* This function start two tasks, one on each CPU. These tasks are started
|
||||
* with high priority. These tasks are normally inactive, waiting until one of
|
||||
* the esp_ipc_call_* functions to be used. One of these tasks will be
|
||||
* woken up to execute the callback provided to esp_ipc_call_nonblocking or
|
||||
* esp_ipc_call_blocking.
|
||||
*/
|
||||
static void esp_ipc_init(void) __attribute__((constructor));
|
||||
|
||||
static void esp_ipc_init(void)
|
||||
{
|
||||
#ifdef CONFIG_ESP_IPC_ISR_ENABLE
|
||||
esp_ipc_isr_init();
|
||||
#endif
|
||||
char task_name[15];
|
||||
|
||||
for (int i = 0; i < portNUM_PROCESSORS; ++i) {
|
||||
snprintf(task_name, sizeof(task_name), "ipc%d", i);
|
||||
s_ipc_mutex[i] = xSemaphoreCreateMutex();
|
||||
s_ipc_ack[i] = xSemaphoreCreateBinary();
|
||||
s_ipc_sem[i] = xSemaphoreCreateBinary();
|
||||
portBASE_TYPE res = xTaskCreatePinnedToCore(ipc_task, task_name, CONFIG_ESP_IPC_TASK_STACK_SIZE, (void*) i,
|
||||
configMAX_PRIORITIES - 1, &s_ipc_task_handle[i], i);
|
||||
assert(res == pdTRUE);
|
||||
(void)res;
|
||||
}
|
||||
}
|
||||
|
||||
static esp_err_t esp_ipc_call_and_wait(uint32_t cpu_id, esp_ipc_func_t func, void* arg, esp_ipc_wait_t wait_for)
|
||||
{
|
||||
if (cpu_id >= portNUM_PROCESSORS) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
if (xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_IPC_USES_CALLERS_PRIORITY
|
||||
TaskHandle_t task_handler = xTaskGetCurrentTaskHandle();
|
||||
UBaseType_t priority_of_current_task = uxTaskPriorityGet(task_handler);
|
||||
UBaseType_t priority_of_running_ipc_task = uxTaskPriorityGet(s_ipc_task_handle[cpu_id]);
|
||||
if (priority_of_running_ipc_task < priority_of_current_task) {
|
||||
vTaskPrioritySet(s_ipc_task_handle[cpu_id], priority_of_current_task);
|
||||
}
|
||||
|
||||
xSemaphoreTake(s_ipc_mutex[cpu_id], portMAX_DELAY);
|
||||
vTaskPrioritySet(s_ipc_task_handle[cpu_id], priority_of_current_task);
|
||||
#else
|
||||
xSemaphoreTake(s_ipc_mutex[0], portMAX_DELAY);
|
||||
#endif
|
||||
|
||||
s_func[cpu_id] = func;
|
||||
s_func_arg[cpu_id] = arg;
|
||||
s_ipc_wait[cpu_id] = wait_for;
|
||||
xSemaphoreGive(s_ipc_sem[cpu_id]);
|
||||
xSemaphoreTake(s_ipc_ack[cpu_id], portMAX_DELAY);
|
||||
s_func[cpu_id] = NULL;
|
||||
#ifdef CONFIG_ESP_IPC_USES_CALLERS_PRIORITY
|
||||
xSemaphoreGive(s_ipc_mutex[cpu_id]);
|
||||
#else
|
||||
xSemaphoreGive(s_ipc_mutex[0]);
|
||||
#endif
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_ipc_call(uint32_t cpu_id, esp_ipc_func_t func, void* arg)
|
||||
{
|
||||
return esp_ipc_call_and_wait(cpu_id, func, arg, IPC_WAIT_FOR_START);
|
||||
}
|
||||
|
||||
esp_err_t esp_ipc_call_blocking(uint32_t cpu_id, esp_ipc_func_t func, void* arg)
|
||||
{
|
||||
return esp_ipc_call_and_wait(cpu_id, func, arg, IPC_WAIT_FOR_END);
|
||||
}
|
||||
|
||||
// currently this is only called from gcov component
|
||||
#if CONFIG_APPTRACE_GCOV_ENABLE
|
||||
esp_err_t esp_ipc_start_gcov_from_isr(uint32_t cpu_id, esp_ipc_func_t func, void* arg)
|
||||
{
|
||||
portBASE_TYPE ret = pdFALSE;
|
||||
|
||||
if (xTaskGetSchedulerState() != taskSCHEDULER_RUNNING) {
|
||||
return ESP_ERR_INVALID_STATE;
|
||||
}
|
||||
|
||||
/* Lock IPC to avoid interferring with normal IPC calls, e.g.
|
||||
avoid situation when esp_ipc_start_gcov_from_isr() is called from IRQ
|
||||
in the middle of IPC call between `s_func` and `s_func_arg` modification. See esp_ipc_call_and_wait() */
|
||||
#ifdef CONFIG_ESP_IPC_USES_CALLERS_PRIORITY
|
||||
ret = xSemaphoreTakeFromISR(s_ipc_mutex[cpu_id], NULL);
|
||||
#else
|
||||
ret = xSemaphoreTakeFromISR(s_ipc_mutex[0], NULL);
|
||||
#endif
|
||||
if (ret != pdTRUE) {
|
||||
return ESP_ERR_TIMEOUT;
|
||||
}
|
||||
|
||||
s_gcov_func = func;
|
||||
s_gcov_func_arg = arg;
|
||||
ret = xSemaphoreGiveFromISR(s_ipc_sem[cpu_id], NULL);
|
||||
|
||||
#ifdef CONFIG_ESP_IPC_USES_CALLERS_PRIORITY
|
||||
xSemaphoreGiveFromISR(s_ipc_mutex[cpu_id], NULL);
|
||||
#else
|
||||
xSemaphoreGiveFromISR(s_ipc_mutex[0], NULL);
|
||||
#endif
|
||||
|
||||
return ret == pdTRUE ? ESP_OK : ESP_FAIL;
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif // not CONFIG_FREERTOS_UNICORE or CONFIG_APPTRACE_GCOV_ENABLE
|
89
components/esp_system/include/esp_ipc.h
Normal file
89
components/esp_system/include/esp_ipc.h
Normal file
@@ -0,0 +1,89 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#ifndef __ESP_IPC_H__
|
||||
#define __ESP_IPC_H__
|
||||
|
||||
#include <esp_err.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
#if !defined(CONFIG_FREERTOS_UNICORE) || defined(CONFIG_APPTRACE_GCOV_ENABLE)
|
||||
|
||||
/** @cond */
|
||||
typedef void (*esp_ipc_func_t)(void* arg);
|
||||
/** @endcond */
|
||||
/*
|
||||
* Inter-processor call APIs
|
||||
*
|
||||
* FreeRTOS provides several APIs which can be used to communicate between
|
||||
* different tasks, including tasks running on different CPUs.
|
||||
* This module provides additional APIs to run some code on the other CPU.
|
||||
*
|
||||
* These APIs can only be used when FreeRTOS scheduler is running.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief 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.
|
||||
*
|
||||
* @param[in] cpu_id CPU where the given function should be executed (0 or 1)
|
||||
* @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
|
||||
*
|
||||
* @return
|
||||
* - 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(uint32_t cpu_id, esp_ipc_func_t func, void* arg);
|
||||
|
||||
|
||||
/**
|
||||
* @brief 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.
|
||||
*
|
||||
* @param[in] cpu_id CPU where the given function should be executed (0 or 1)
|
||||
* @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
|
||||
*
|
||||
* @return
|
||||
* - 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);
|
||||
|
||||
#endif // not CONFIG_FREERTOS_UNICORE or CONFIG_APPTRACE_GCOV_ENABLE
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* __ESP_IPC_H__ */
|
117
components/esp_system/include/esp_ipc_isr.h
Normal file
117
components/esp_system/include/esp_ipc_isr.h
Normal file
@@ -0,0 +1,117 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 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
|
||||
|
||||
/** @cond */
|
||||
typedef void (*esp_ipc_isr_func_t)(void* arg);
|
||||
/** @endcond */
|
||||
|
||||
/**
|
||||
* @brief Initialize inter-processor call module which based on #4 high-interrupt.
|
||||
*
|
||||
* This function is called on CPU start and should not be called from the application.
|
||||
*
|
||||
* This function starts two tasks, one on each CPU. These tasks register
|
||||
* #4 High-interrupt and after that, the tasks are deleted.
|
||||
* The next API functions work with this functionality:
|
||||
* esp_ipc_isr_asm_call
|
||||
* esp_ipc_isr_asm_call_blocking
|
||||
* They allow to run an asm function on other CPU.
|
||||
*/
|
||||
void esp_ipc_isr_init(void);
|
||||
|
||||
/**
|
||||
* @brief Execute an asm function on the other CPU (uses the #4 high-priority interrupt)
|
||||
*
|
||||
* @note In single-core mode, it is not available.
|
||||
* This function calls the #4 high-priority interrupt on the other CPU.
|
||||
* The given function is called in the context of the interrupt by CALLX0 command and
|
||||
* operates with registers a2, a3, a4.
|
||||
*
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @brief Execute an asm function on the other CPU and blocks until it completes (uses the #4 high-priority interrupt)
|
||||
*
|
||||
* @note In single-core mode, it is not available.
|
||||
* This function calls the #4 high-priority interrupt on the other CPU.
|
||||
* The given function is called in the context of the interrupt by CALLX0 command.
|
||||
*
|
||||
* @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);
|
||||
|
||||
/**
|
||||
* @brief Stall the other CPU and the current CPU disables interrupts with level 3 and lower.
|
||||
*
|
||||
* @note In single-core mode, it is not available.
|
||||
* This function calls the #4 high-priority interrupt on the other CPU.
|
||||
* The esp_ipc_isr_finish_cmd() function is called on the other CPU in the context of the #4 high-priority interrupt.
|
||||
* The esp_ipc_isr_finish_cmd is called by CALLX0 command.
|
||||
* It is waiting for the end command. The command will be sent by esp_ipc_isr_release_other_cpu().
|
||||
* This function is used for DPORT workaround.
|
||||
*
|
||||
* This function blocks other CPU until the release call esp_ipc_isr_release_other_cpu().
|
||||
*
|
||||
* This fucntion is used for the DPORT workaround: stall other cpu that this cpu is pending to access dport register start.
|
||||
*/
|
||||
void esp_ipc_isr_stall_other_cpu(void);
|
||||
|
||||
/**
|
||||
* @brief Release the other CPU
|
||||
*
|
||||
* @note In single-core mode, it is not available.
|
||||
* This function will send the end command to release the stall other CPU.
|
||||
* This function is used for DPORT workaround: stall other cpu that this cpu is pending to access dport register end.
|
||||
*
|
||||
*/
|
||||
void esp_ipc_isr_release_other_cpu(void);
|
||||
|
||||
/**
|
||||
* @brief Pause stall the other CPU
|
||||
*/
|
||||
void esp_ipc_isr_stall_pause(void);
|
||||
|
||||
/**
|
||||
* @brief Abort stall the other CPU
|
||||
*
|
||||
* This routine does not stop the stall routines in any way that is recoverable.
|
||||
* Please only call in case of panic().
|
||||
* Used in panic code: the enter_critical stuff may be messed up so we just stop everything without checking the mux.
|
||||
*/
|
||||
void esp_ipc_isr_stall_abort(void);
|
||||
|
||||
/**
|
||||
* @brief Resume stall the other CPU
|
||||
*/
|
||||
void esp_ipc_isr_stall_resume(void);
|
||||
|
||||
#else // not CONFIG_ESP_IPC_ISR_ENABLE
|
||||
|
||||
#define esp_ipc_isr_stall_other_cpu()
|
||||
#define esp_ipc_isr_release_other_cpu()
|
||||
#define esp_ipc_isr_stall_pause()
|
||||
#define esp_ipc_isr_stall_abort()
|
||||
#define esp_ipc_isr_stall_resume()
|
||||
|
||||
#endif // CONFIG_ESP_IPC_ISR_ENABLE
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
215
components/esp_system/port/arch/xtensa/esp_ipc_isr.c
Normal file
215
components/esp_system/port/arch/xtensa/esp_ipc_isr.c
Normal file
@@ -0,0 +1,215 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stddef.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_attr.h"
|
||||
#include "soc/cpu.h"
|
||||
#include "soc/soc.h"
|
||||
#include "soc/dport_access.h"
|
||||
#ifdef CONFIG_IDF_TARGET_ESP32
|
||||
#include "soc/dport_reg.h"
|
||||
#else
|
||||
#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_ipc_isr.h"
|
||||
#include "xtensa/core-macros.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
static portMUX_TYPE s_ipc_isr_mux = portMUX_INITIALIZER_UNLOCKED;
|
||||
uint32_t volatile esp_ipc_isr_start_fl; // the flag shows that it is about to run esp_ipc_func()
|
||||
uint32_t volatile esp_ipc_isr_end_fl = 1; // the flag shows that esp_ipc_func() is done
|
||||
esp_ipc_isr_func_t volatile esp_ipc_func; // the function which will be run in the ipc_isr context
|
||||
void * volatile esp_ipc_func_arg; // the argument of esp_ipc_func()
|
||||
|
||||
typedef enum {
|
||||
STALL_STATE_IDLE = 0,
|
||||
STALL_STATE_RUNNING = 1,
|
||||
} stall_state_t;
|
||||
|
||||
static stall_state_t volatile s_stall_state = STALL_STATE_IDLE;
|
||||
static int32_t volatile s_count_of_nested_calls[portNUM_PROCESSORS] = { 0 };
|
||||
static BaseType_t s_stored_interrupt_level;
|
||||
static uint32_t volatile esp_ipc_isr_finish_cmd;
|
||||
|
||||
|
||||
/**
|
||||
* @brief Type of calling
|
||||
*/
|
||||
typedef enum {
|
||||
IPC_ISR_WAIT_FOR_START = 0, /*!< The caller is waiting for the start */
|
||||
IPC_ISR_WAIT_FOR_END = 1, /*!< The caller is waiting for the end */
|
||||
} esp_ipc_isr_wait_t;
|
||||
|
||||
#define IPC_ISR_ENTER_CRITICAL() portENTER_CRITICAL_SAFE(&s_ipc_isr_mux)
|
||||
#define IPC_ISR_EXIT_CRITICAL() portEXIT_CRITICAL_SAFE(&s_ipc_isr_mux)
|
||||
|
||||
static void esp_ipc_isr_call_and_wait(esp_ipc_isr_func_t func, void* arg, esp_ipc_isr_wait_t wait_for);
|
||||
|
||||
|
||||
/* Initializing IPC_ISR */
|
||||
|
||||
static void esp_ipc_isr_init_cpu(void* arg)
|
||||
{
|
||||
(void) arg;
|
||||
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);
|
||||
intr_matrix_set(cpuid, intr_source, ETS_IPC_ISR_INUM);
|
||||
ESP_INTR_ENABLE(ETS_IPC_ISR_INUM);
|
||||
|
||||
/* If this fails then the minimum stack size for this config is too close to running out */
|
||||
assert(uxTaskGetStackHighWaterMark(NULL) > 128);
|
||||
|
||||
if (cpuid != 0) {
|
||||
s_stall_state = STALL_STATE_RUNNING;
|
||||
}
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
void esp_ipc_isr_init(void)
|
||||
{
|
||||
for (unsigned i = 0; i < portNUM_PROCESSORS; ++i) {
|
||||
portBASE_TYPE res = xTaskCreatePinnedToCore(esp_ipc_isr_init_cpu, "ipc_isr_init", configMINIMAL_STACK_SIZE, NULL, 5, NULL, i);
|
||||
assert(res == pdTRUE);
|
||||
(void)res;
|
||||
}
|
||||
}
|
||||
|
||||
/* End initializing IPC_ISR */
|
||||
|
||||
|
||||
/* Public API functions */
|
||||
|
||||
void IRAM_ATTR esp_ipc_isr_asm_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)
|
||||
{
|
||||
IPC_ISR_ENTER_CRITICAL();
|
||||
esp_ipc_isr_call_and_wait(func, arg, IPC_ISR_WAIT_FOR_END);
|
||||
IPC_ISR_EXIT_CRITICAL();
|
||||
}
|
||||
|
||||
// This asm function is from esp_ipc_isr_routines.S.
|
||||
// It is waiting for the finish_cmd command in a loop.
|
||||
void esp_ipc_isr_waiting_for_finish_cmd(void* finish_cmd);
|
||||
|
||||
/*
|
||||
* esp_ipc_isr_stall_other_cpu is used for:
|
||||
* - stall other CPU,
|
||||
* - do protection when dual core access DPORT internal register and APB register via DPORT simultaneously.
|
||||
* This function will be initialize after FreeRTOS startup.
|
||||
* When cpu0 wants to access DPORT register, it should notify cpu1 enter in high-priority interrupt for be mute.
|
||||
* When cpu1 already in high-priority interrupt, cpu0 can access DPORT register.
|
||||
* Currently, cpu1 will wait for cpu0 finish access and exit high-priority interrupt.
|
||||
*/
|
||||
void IRAM_ATTR esp_ipc_isr_stall_other_cpu(void)
|
||||
{
|
||||
if (s_stall_state == STALL_STATE_RUNNING) {
|
||||
BaseType_t intLvl = portSET_INTERRUPT_MASK_FROM_ISR();
|
||||
const uint32_t cpu_id = xPortGetCoreID();
|
||||
if (s_count_of_nested_calls[cpu_id]++ == 0) {
|
||||
IPC_ISR_ENTER_CRITICAL();
|
||||
s_stored_interrupt_level = intLvl;
|
||||
esp_ipc_isr_finish_cmd = 0;
|
||||
esp_ipc_isr_call_and_wait(&esp_ipc_isr_waiting_for_finish_cmd, (void*)&esp_ipc_isr_finish_cmd, IPC_ISR_WAIT_FOR_START);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Interrupts are already disabled by the parent, we're nested here. */
|
||||
portCLEAR_INTERRUPT_MASK_FROM_ISR(intLvl);
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR esp_ipc_isr_release_other_cpu(void)
|
||||
{
|
||||
if (s_stall_state == STALL_STATE_RUNNING) {
|
||||
const uint32_t cpu_id = xPortGetCoreID();
|
||||
if (--s_count_of_nested_calls[cpu_id] == 0) {
|
||||
esp_ipc_isr_finish_cmd = 1;
|
||||
IPC_ISR_EXIT_CRITICAL();
|
||||
portCLEAR_INTERRUPT_MASK_FROM_ISR(s_stored_interrupt_level);
|
||||
} else if (s_count_of_nested_calls[cpu_id] < 0) {
|
||||
assert(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IRAM_ATTR esp_ipc_isr_stall_pause(void)
|
||||
{
|
||||
IPC_ISR_ENTER_CRITICAL();
|
||||
s_stall_state = STALL_STATE_IDLE;
|
||||
IPC_ISR_EXIT_CRITICAL();
|
||||
}
|
||||
|
||||
void IRAM_ATTR esp_ipc_isr_stall_abort(void)
|
||||
{
|
||||
s_stall_state = STALL_STATE_IDLE;
|
||||
}
|
||||
|
||||
void IRAM_ATTR esp_ipc_isr_stall_resume(void)
|
||||
{
|
||||
IPC_ISR_ENTER_CRITICAL();
|
||||
s_stall_state = STALL_STATE_RUNNING;
|
||||
IPC_ISR_EXIT_CRITICAL();
|
||||
}
|
||||
|
||||
void esp_dport_access_stall_other_cpu_start(void) __attribute__((alias("esp_ipc_isr_stall_other_cpu")));
|
||||
void esp_dport_access_stall_other_cpu_end(void) __attribute__((alias("esp_ipc_isr_release_other_cpu")));
|
||||
void esp_dport_access_int_pause(void) __attribute__((alias("esp_ipc_isr_stall_pause")));
|
||||
void esp_dport_access_int_abort(void) __attribute__((alias("esp_ipc_isr_stall_abort")));
|
||||
void esp_dport_access_int_resume(void) __attribute__((alias("esp_ipc_isr_stall_resume")));
|
||||
|
||||
/* End public API functions */
|
||||
|
||||
|
||||
/* Private functions*/
|
||||
|
||||
static void IRAM_ATTR esp_ipc_isr_call_and_wait(esp_ipc_isr_func_t func, void* arg, esp_ipc_isr_wait_t wait_for)
|
||||
{
|
||||
const uint32_t cpu_id = xPortGetCoreID();
|
||||
|
||||
// waiting for the end of the previous call
|
||||
while (!esp_ipc_isr_end_fl) {};
|
||||
|
||||
esp_ipc_func = func;
|
||||
esp_ipc_func_arg = arg;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
// IPC_ISR handler will be called and `...isr_start` and `...isr_end` will be updated there
|
||||
|
||||
if (wait_for == IPC_ISR_WAIT_FOR_START) {
|
||||
while (!esp_ipc_isr_start_fl) {};
|
||||
} else {
|
||||
// IPC_ISR_WAIT_FOR_END
|
||||
while (!esp_ipc_isr_end_fl) {};
|
||||
}
|
||||
}
|
||||
|
||||
/* End private functions*/
|
121
components/esp_system/port/arch/xtensa/esp_ipc_isr_handler.S
Normal file
121
components/esp_system/port/arch/xtensa/esp_ipc_isr_handler.S
Normal file
@@ -0,0 +1,121 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <xtensa/coreasm.h>
|
||||
#include <xtensa/corebits.h>
|
||||
#include <xtensa/config/system.h>
|
||||
#include "freertos/xtensa_context.h"
|
||||
#include "esp_private/panic_reason.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "soc/soc.h"
|
||||
#include "soc/dport_reg.h"
|
||||
|
||||
/* High-priority interrupt - IPC_ISR handler */
|
||||
|
||||
#if CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5
|
||||
#define LX_INTR_STACK_SIZE 16
|
||||
#define LX_INTR_A0_OFFSET 0
|
||||
#define LX_INTR_A2_OFFSET 4
|
||||
#define LX_INTR_A3_OFFSET 8
|
||||
#define LX_INTR_A4_OFFSET 12
|
||||
#define EXCSAVE_X EXCSAVE_5
|
||||
#define RFI_X 5
|
||||
|
||||
#elif CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4
|
||||
|
||||
#define LX_INTR_STACK_SIZE 16
|
||||
#define LX_INTR_A0_OFFSET 0
|
||||
#define LX_INTR_A2_OFFSET 4
|
||||
#define LX_INTR_A3_OFFSET 8
|
||||
#define LX_INTR_A4_OFFSET 12
|
||||
#define EXCSAVE_X EXCSAVE_4
|
||||
#define RFI_X 4
|
||||
|
||||
#endif /* CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */
|
||||
|
||||
.data
|
||||
_lx_intr_stack:
|
||||
.space LX_INTR_STACK_SIZE
|
||||
.section .iram1,"ax"
|
||||
.global esp_ipc_isr_handler
|
||||
.type esp_ipc_isr_handler,@function
|
||||
.align 4
|
||||
esp_ipc_isr_handler:
|
||||
/* Allocate exception frame and save minimal context. */
|
||||
/* Because the interrupt cause code has protection that only
|
||||
allows one cpu to enter in the IPC_ISR section of the LX
|
||||
interrupt at one time, there's no need to have two
|
||||
_lx_intr_stack for each cpu */
|
||||
|
||||
/* Save A0, A2, A3, A4 so we can use those registers further*/
|
||||
movi a0, _lx_intr_stack
|
||||
s32i a2, a0, LX_INTR_A2_OFFSET
|
||||
s32i a3, a0, LX_INTR_A3_OFFSET
|
||||
s32i a4, a0, LX_INTR_A4_OFFSET
|
||||
rsr a2, EXCSAVE_X
|
||||
s32i a2, a0, LX_INTR_A0_OFFSET
|
||||
|
||||
/* disable nested iterrupts */
|
||||
/* PS.EXCM is changed from 1 to 0 . It allows using usually exception handler instead of the Double exception handler. */
|
||||
/* PS_UM = 1 */
|
||||
movi a0, PS_INTLEVEL(5) | PS_UM
|
||||
wsr a0, PS
|
||||
rsync
|
||||
/* restore PS will be done by rfi the end */
|
||||
|
||||
/*
|
||||
* Reset isr interrupt flags
|
||||
*/
|
||||
#if CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5
|
||||
/* This int is level-triggered and doesn't need clearing.
|
||||
Do nothing here and clear int status by peripheral register later.*/
|
||||
#elif CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4
|
||||
/* This int is edge-triggered and needs clearing. */
|
||||
movi a3, (1 << ETS_IPC_ISR_INUM)
|
||||
wsr a3, INTCLEAR
|
||||
#endif /* CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5 */
|
||||
|
||||
/* get CORE_ID */
|
||||
getcoreid a3
|
||||
beqz a3, 1f
|
||||
|
||||
/* current cpu is 1 */
|
||||
movi a3, SYSTEM_CPU_INTR_FROM_CPU_3_REG
|
||||
movi a4, 0
|
||||
s32i a4, a3, 0 /* clear intr */
|
||||
j 2f
|
||||
1:
|
||||
/* current cpu is 0 */
|
||||
movi a3, SYSTEM_CPU_INTR_FROM_CPU_2_REG
|
||||
movi a4, 0
|
||||
s32i a4, a3, 0 /* clear intr */
|
||||
2:
|
||||
|
||||
/* set the start flag */
|
||||
movi a0, esp_ipc_isr_start_fl
|
||||
s32i a0, a0, 0
|
||||
|
||||
/* Call the esp_ipc_function(void* arg) */
|
||||
movi a0, esp_ipc_func
|
||||
l32i a0, a0, 0
|
||||
movi a2, esp_ipc_func_arg
|
||||
l32i a2, a2, 0
|
||||
callx0 a0
|
||||
|
||||
/* Done. Restore registers and return. */
|
||||
movi a0, _lx_intr_stack
|
||||
l32i a2, a0, LX_INTR_A2_OFFSET
|
||||
l32i a3, a0, LX_INTR_A3_OFFSET
|
||||
l32i a4, a0, LX_INTR_A4_OFFSET
|
||||
|
||||
/* set the end flag */
|
||||
movi a0, esp_ipc_isr_end_fl
|
||||
s32i a0, a0, 0
|
||||
|
||||
/* restore a0 */
|
||||
rsr a0, EXCSAVE_X
|
||||
/* restores PS from EPS[X] and jumps to the address in EPC[X] */
|
||||
rfi RFI_X
|
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <xtensa/coreasm.h>
|
||||
#include <xtensa/corebits.h>
|
||||
#include <xtensa/config/system.h>
|
||||
#include <xtensa/hal.h>
|
||||
|
||||
/* esp_ipc_isr_waiting_for_finish_cmd(void* finish_cmd)
|
||||
*
|
||||
* It should be called by the CALLX0 command from the handler of High-priority interrupt (4 lvl).
|
||||
* Only these registers [a2, a3, a4] can be used here.
|
||||
*/
|
||||
.section .iram1, "ax"
|
||||
.align 4
|
||||
.global esp_ipc_isr_waiting_for_finish_cmd
|
||||
.type esp_ipc_isr_waiting_for_finish_cmd, @function
|
||||
// Args:
|
||||
// a2 - finish_cmd (pointer on esp_ipc_isr_finish_cmd)
|
||||
esp_ipc_isr_waiting_for_finish_cmd:
|
||||
/* waiting for the finish command */
|
||||
.check_finish_cmd:
|
||||
l32i a3, a2, 0
|
||||
beqz a3, .check_finish_cmd
|
||||
ret
|
@@ -12,6 +12,13 @@ set(srcs "highint_hdl.S"
|
||||
"../../arch/xtensa/debug_stubs.c"
|
||||
"../../arch/xtensa/trax.c"
|
||||
)
|
||||
|
||||
if(CONFIG_ESP_IPC_ISR_ENABLE)
|
||||
list(APPEND srcs "../../arch/xtensa/esp_ipc_isr.c"
|
||||
"../../arch/xtensa/esp_ipc_isr_handler.S"
|
||||
"../../arch/xtensa/esp_ipc_isr_routines.S")
|
||||
endif()
|
||||
|
||||
add_prefix(srcs "${CMAKE_CURRENT_LIST_DIR}/" ${srcs})
|
||||
|
||||
target_sources(${COMPONENT_LIB} PRIVATE ${srcs})
|
||||
|
@@ -12,6 +12,13 @@ set(srcs "highint_hdl.S"
|
||||
"../../arch/xtensa/debug_stubs.c"
|
||||
"../../arch/xtensa/trax.c"
|
||||
)
|
||||
|
||||
if(CONFIG_ESP_IPC_ISR_ENABLE)
|
||||
list(APPEND srcs "../../arch/xtensa/esp_ipc_isr.c"
|
||||
"../../arch/xtensa/esp_ipc_isr_handler.S"
|
||||
"../../arch/xtensa/esp_ipc_isr_routines.S")
|
||||
endif()
|
||||
|
||||
add_prefix(srcs "${CMAKE_CURRENT_LIST_DIR}/" ${srcs})
|
||||
|
||||
target_sources(${COMPONENT_LIB} PRIVATE ${srcs})
|
||||
|
@@ -13,6 +13,13 @@ set(srcs "highint_hdl.S"
|
||||
"../../arch/xtensa/debug_stubs.c"
|
||||
"../../arch/xtensa/trax.c"
|
||||
)
|
||||
|
||||
if(CONFIG_ESP_IPC_ISR_ENABLE)
|
||||
list(APPEND srcs "../../arch/xtensa/esp_ipc_isr.c"
|
||||
"../../arch/xtensa/esp_ipc_isr_handler.S"
|
||||
"../../arch/xtensa/esp_ipc_isr_routines.S")
|
||||
endif()
|
||||
|
||||
add_prefix(srcs "${CMAKE_CURRENT_LIST_DIR}/" ${srcs})
|
||||
|
||||
target_sources(${COMPONENT_LIB} PRIVATE ${srcs})
|
||||
|
@@ -1,3 +1,20 @@
|
||||
set(requires "unity"
|
||||
"test_utils"
|
||||
"driver")
|
||||
|
||||
set(excludes "test_ipc_isr.c"
|
||||
"test_ipc_isr.S"
|
||||
"test_ipc.c")
|
||||
|
||||
if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s3")
|
||||
# If the target is esp32 or esp32s3, we can compile the IPC
|
||||
# tests.
|
||||
set(excludes "")
|
||||
# Add a required dependency
|
||||
list(APPEND requires "cmock")
|
||||
endif()
|
||||
|
||||
idf_component_register(SRC_DIRS .
|
||||
EXCLUDE_SRCS "${excludes}"
|
||||
PRIV_INCLUDE_DIRS .
|
||||
PRIV_REQUIRES unity test_utils driver)
|
||||
PRIV_REQUIRES "${requires}")
|
||||
|
134
components/esp_system/test/test_ipc.c
Normal file
134
components/esp_system/test/test_ipc.c
Normal file
@@ -0,0 +1,134 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "unity.h"
|
||||
#if !CONFIG_FREERTOS_UNICORE
|
||||
#include "esp_ipc.h"
|
||||
#endif
|
||||
#include "esp_log.h"
|
||||
#include "esp_rom_sys.h"
|
||||
|
||||
#if !CONFIG_FREERTOS_UNICORE
|
||||
static void test_func_ipc_cb(void *arg)
|
||||
{
|
||||
vTaskDelay(50);
|
||||
int *val = (int *)arg;
|
||||
*val = 0xa5a5;
|
||||
}
|
||||
|
||||
TEST_CASE("Test blocking IPC function call", "[ipc]")
|
||||
{
|
||||
int val = 0x5a5a;
|
||||
esp_ipc_call_blocking(!xPortGetCoreID(), test_func_ipc_cb, &val);
|
||||
TEST_ASSERT_EQUAL_HEX(val, 0xa5a5);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_ESP_IPC_USES_CALLERS_PRIORITY
|
||||
static volatile bool exit_flag;
|
||||
|
||||
static void task1(void *sema)
|
||||
{
|
||||
ESP_LOGI("task1", "start");
|
||||
esp_rom_delay_us(3000000);
|
||||
vTaskDelay(1);
|
||||
while (exit_flag == false) {
|
||||
|
||||
}
|
||||
ESP_LOGI("task1", "finish");
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
static UBaseType_t func_ipc_priority;
|
||||
|
||||
static void test_func_ipc(void *sema)
|
||||
{
|
||||
esp_rom_delay_us(1000000 + xPortGetCoreID() * 100);
|
||||
func_ipc_priority = uxTaskPriorityGet(NULL);
|
||||
xSemaphoreGive(*(xSemaphoreHandle *)sema);
|
||||
esp_rom_printf("test_func_ipc: [%d, %d]\n", func_ipc_priority, xPortGetCoreID());
|
||||
}
|
||||
|
||||
TEST_CASE("Test ipc_task works with the priority of the caller's task", "[ipc]")
|
||||
{
|
||||
UBaseType_t priority = 18;
|
||||
func_ipc_priority = 0;
|
||||
vTaskPrioritySet(NULL, priority);
|
||||
|
||||
xSemaphoreHandle sema_ipc_done = xSemaphoreCreateBinary();
|
||||
|
||||
exit_flag = false;
|
||||
xTaskCreatePinnedToCore(task1, "task1", 4096, NULL, priority + 2, NULL, 1);
|
||||
vTaskDelay(100 / portTICK_PERIOD_MS);
|
||||
|
||||
ESP_LOGI("test", "Start IPC call in IPC_WAIT_FOR_START mode");
|
||||
esp_ipc_call(1, test_func_ipc, &sema_ipc_done);
|
||||
ESP_LOGI("test", "Waiting for IPC finish");
|
||||
xSemaphoreTake(sema_ipc_done, 4000 / portTICK_PERIOD_MS);
|
||||
ESP_LOGI("test", "Stop task1");
|
||||
exit_flag = true;
|
||||
xSemaphoreTake(sema_ipc_done, portMAX_DELAY);
|
||||
vSemaphoreDelete(sema_ipc_done);
|
||||
ESP_LOGI("test", "Check ipc_priority with priority caller's task. Should be the same");
|
||||
vTaskPrioritySet(NULL, 5);
|
||||
TEST_ASSERT_EQUAL(priority, func_ipc_priority);
|
||||
}
|
||||
|
||||
static void test_func2_ipc(void *arg)
|
||||
{
|
||||
int callers_priority = *(int *)arg;
|
||||
esp_rom_delay_us(1000000 + xPortGetCoreID() * 100);
|
||||
UBaseType_t priority = uxTaskPriorityGet(NULL);
|
||||
esp_rom_printf("test_func2_ipc: [callers_priority = %d, priority = %d, cpu = %d]\n", callers_priority, priority, xPortGetCoreID());
|
||||
}
|
||||
|
||||
static void task(void *sema)
|
||||
{
|
||||
int priority = uxTaskPriorityGet(NULL);
|
||||
ESP_LOGI("task", "start [priority = %d, cpu = %d]", priority, xPortGetCoreID());
|
||||
xSemaphoreTake(*(xSemaphoreHandle *)sema, portMAX_DELAY);
|
||||
esp_ipc_call_blocking(!xPortGetCoreID(), test_func2_ipc, &priority);
|
||||
xSemaphoreGive(*(xSemaphoreHandle *)sema);
|
||||
ESP_LOGI("task", "finish [priority = %d, cpu = %d]", priority, xPortGetCoreID());
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
TEST_CASE("Test multiple ipc_calls", "[ipc]")
|
||||
{
|
||||
const int max_tasks = 5;
|
||||
UBaseType_t priority = uxTaskPriorityGet(NULL);
|
||||
ESP_LOGI("test", "priority = %d, cpu = %d", priority, xPortGetCoreID());
|
||||
xSemaphoreHandle sema_ipc_done[max_tasks * portNUM_PROCESSORS];
|
||||
|
||||
for (int task_num = 0; task_num < max_tasks; ++task_num) {
|
||||
++priority;
|
||||
ESP_LOGI("test", "task prio = %d", priority);
|
||||
for (int cpu_num = 0; cpu_num < portNUM_PROCESSORS; ++cpu_num) {
|
||||
sema_ipc_done[task_num * 2 + cpu_num] = xSemaphoreCreateBinary();
|
||||
xTaskCreatePinnedToCore(task, "task", 4096, &sema_ipc_done[task_num * 2 + cpu_num], priority, NULL, cpu_num);
|
||||
}
|
||||
}
|
||||
|
||||
for (int task_num = 0; task_num < max_tasks; ++task_num) {
|
||||
for (int cpu_num = 0; cpu_num < portNUM_PROCESSORS; ++cpu_num) {
|
||||
xSemaphoreGive(sema_ipc_done[task_num * 2 + cpu_num]);
|
||||
}
|
||||
}
|
||||
|
||||
for (int task_num = 0; task_num < max_tasks; ++task_num) {
|
||||
for (int cpu_num = 0; cpu_num < portNUM_PROCESSORS; ++cpu_num) {
|
||||
xSemaphoreTake(sema_ipc_done[task_num * 2 + cpu_num], portMAX_DELAY);
|
||||
vSemaphoreDelete(sema_ipc_done[task_num * 2 + cpu_num]);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif /* CONFIG_ESP_IPC_USE_CALLERS_PRIORITY */
|
||||
|
||||
#endif /* !CONFIG_FREERTOS_UNICORE */
|
61
components/esp_system/test/test_ipc_isr.S
Normal file
61
components/esp_system/test/test_ipc_isr.S
Normal file
@@ -0,0 +1,61 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <xtensa/coreasm.h>
|
||||
#include <xtensa/corebits.h>
|
||||
#include <xtensa/config/system.h>
|
||||
#include <xtensa/hal.h>
|
||||
|
||||
/* esp_test_ipc_isr_asm(void *arg)
|
||||
*
|
||||
* It should be called by the CALLX0 command from the handler of High-priority interrupt (4 lvl).
|
||||
* 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
|
||||
// Args:
|
||||
// a2 - void* arg
|
||||
esp_test_ipc_isr_asm:
|
||||
movi a3, 0xa5a5
|
||||
s32i a3, a2, 0
|
||||
ret
|
||||
|
||||
|
||||
/* esp_test_ipc_isr_get_other_core_id(void *arg)
|
||||
*
|
||||
* this function puts the core_id of the other CPU in the arg.
|
||||
* use only a2, a3 and a4 regs here.
|
||||
*/
|
||||
.section .iram1, "ax"
|
||||
.align 4
|
||||
.global esp_test_ipc_isr_get_other_core_id
|
||||
.type esp_test_ipc_isr_get_other_core_id, @function
|
||||
// Args:
|
||||
// a2 - void* arg
|
||||
esp_test_ipc_isr_get_other_core_id:
|
||||
rsr.prid a3
|
||||
extui a3, a3, 13, 1
|
||||
s32i a3, a2, 0
|
||||
ret
|
||||
|
||||
|
||||
/* esp_test_ipc_isr_get_cycle_count_other_cpu(void *arg)
|
||||
*
|
||||
* this function puts CCOUNT of the other CPU in the 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
|
91
components/esp_system/test/test_ipc_isr.c
Normal file
91
components/esp_system/test/test_ipc_isr.c
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include "sdkconfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "unity.h"
|
||||
#include "test_utils.h"
|
||||
#include "esp_rom_sys.h"
|
||||
#include "esp_ipc_isr.h"
|
||||
|
||||
#ifdef CONFIG_ESP_IPC_ISR_ENABLE
|
||||
|
||||
void esp_test_ipc_isr_asm(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);
|
||||
TEST_ASSERT_EQUAL_HEX(val, 0xa5a5);
|
||||
}
|
||||
|
||||
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);
|
||||
TEST_ASSERT_EQUAL_HEX(val, 1);
|
||||
}
|
||||
|
||||
TEST_CASE("Test ipc_isr exception in asm func leads to StoreProhibited not to Unhandled debug exception", "[ipc][reset=StoreProhibited,SW_CPU_RESET]")
|
||||
{
|
||||
esp_ipc_isr_asm_call_blocking(esp_test_ipc_isr_asm, NULL);
|
||||
}
|
||||
|
||||
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_rom_printf("CCOUNT CPU0 = %d\n", cpu_ll_get_cycle_count());
|
||||
esp_rom_printf("CCOUNT CPU1 = %d\n", val);
|
||||
}
|
||||
|
||||
static bool volatile s_stop;
|
||||
|
||||
static void task_asm(void *arg)
|
||||
{
|
||||
xSemaphoreHandle *sema = (xSemaphoreHandle *) arg;
|
||||
int val;
|
||||
int counter = 0;
|
||||
printf("task_asm\n");
|
||||
while (s_stop == false) {
|
||||
val = 0x5a5a;
|
||||
esp_ipc_isr_asm_call_blocking(esp_test_ipc_isr_asm, &val);
|
||||
TEST_ASSERT_EQUAL_HEX(val, 0xa5a5);
|
||||
++counter;
|
||||
}
|
||||
printf("task_asm counter = %d\n", counter);
|
||||
TEST_ASSERT_GREATER_THAN(1000000, counter);
|
||||
xSemaphoreGive(*sema);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
||||
TEST_CASE("Test ipc_isr two tasks use IPC function calls", "[ipc]")
|
||||
{
|
||||
xSemaphoreHandle exit_sema[2];
|
||||
exit_sema[0] = xSemaphoreCreateBinary();
|
||||
exit_sema[1] = xSemaphoreCreateBinary();
|
||||
s_stop = false;
|
||||
printf("Test start\n");
|
||||
xTaskCreatePinnedToCore(task_asm, "task_asm", 2048, &exit_sema[0], UNITY_FREERTOS_PRIORITY - 1, NULL, 0);
|
||||
xTaskCreatePinnedToCore(task_asm, "task_asm", 2048, &exit_sema[1], UNITY_FREERTOS_PRIORITY - 1, NULL, 1);
|
||||
vTaskDelay(5000 / portTICK_PERIOD_MS);
|
||||
s_stop = true;
|
||||
xSemaphoreTake(exit_sema[0], portMAX_DELAY);
|
||||
xSemaphoreTake(exit_sema[1], portMAX_DELAY);
|
||||
printf("Test end\n");
|
||||
|
||||
vSemaphoreDelete(exit_sema[0]);
|
||||
vSemaphoreDelete(exit_sema[1]);
|
||||
}
|
||||
#endif /* CONFIG_ESP_IPC_ISR_ENABLE */
|
Reference in New Issue
Block a user