esp_common: Add API for IPC to run small pieces of code on the other CPU, in the context of the level 4 interrupt

This commit is contained in:
Konstantin Kondrashov
2021-08-03 14:35:29 +08:00
committed by Zim Kalinowski
parent a0c548ccd4
commit 4972605b16
51 changed files with 1062 additions and 379 deletions

View File

@@ -1,2 +1,10 @@
idf_component_register(SRCS "ipc.c"
set(srcs "src/esp_ipc.c")
if(CONFIG_ESP_IPC_ISR_ENABLE)
list(APPEND srcs "src/esp_ipc_isr/esp_ipc_isr.c"
"src/esp_ipc_isr/esp_ipc_isr_handler.S"
"src/esp_ipc_isr/esp_ipc_isr_routines.S")
endif()
idf_component_register(SRCS "${srcs}"
INCLUDE_DIRS "include")

View File

@@ -0,0 +1,39 @@
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 servers 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)

View File

@@ -1,3 +1,9 @@
#
# Component Makefile
#
COMPONENT_SRCDIRS := src
ifdef CONFIG_ESP_IPC_ISR_ENABLE
COMPONENT_SRCDIRS += src/esp_ipc_isr
endif
COMPONENT_ADD_INCLUDEDIRS := include

View File

@@ -20,6 +20,9 @@
#ifdef __cplusplus
extern "C" {
#endif
#ifndef CONFIG_FREERTOS_UNICORE
/** @cond */
typedef void (*esp_ipc_func_t)(void* arg);
/** @endcond */
@@ -85,6 +88,7 @@ esp_err_t esp_ipc_call(uint32_t cpu_id, esp_ipc_func_t func, void* arg);
*/
esp_err_t esp_ipc_call_blocking(uint32_t cpu_id, esp_ipc_func_t func, void* arg);
#endif // not CONFIG_FREERTOS_UNICORE
#ifdef __cplusplus
}

View 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

View File

@@ -18,12 +18,15 @@
#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"
#ifndef CONFIG_FREERTOS_UNICORE
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
@@ -86,6 +89,9 @@ 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);
@@ -144,3 +150,5 @@ 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);
}
#endif // not CONFIG_FREERTOS_UNICORE

View 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 = portENTER_CRITICAL_NESTED();
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. */
portEXIT_CRITICAL_NESTED(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();
portEXIT_CRITICAL_NESTED(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*/

View File

@@ -0,0 +1,100 @@
/*
* 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 */
#define L4_INTR_STACK_SIZE 16
#define L4_INTR_A0_OFFSET 0
#define L4_INTR_A2_OFFSET 4
#define L4_INTR_A3_OFFSET 8
#define L4_INTR_A4_OFFSET 12
.data
_l4_intr_stack:
.space L4_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 L4
interrupt at one time, there's no need to have two
_l4_intr_stack for each cpu */
/* Save A0, A2, A3, A4 so we can use those registers further*/
movi a0, _l4_intr_stack
s32i a2, a0, L4_INTR_A2_OFFSET
s32i a3, a0, L4_INTR_A3_OFFSET
s32i a4, a0, L4_INTR_A4_OFFSET
rsr a2, EXCSAVE_4
s32i a2, a0, L4_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
*/
/* This int is edge-triggered and needs clearing. */
movi a3, (1 << ETS_IPC_ISR_INUM)
wsr a3, INTCLEAR
/* 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, _l4_intr_stack
l32i a2, a0, L4_INTR_A2_OFFSET
l32i a3, a0, L4_INTR_A3_OFFSET
l32i a4, a0, L4_INTR_A4_OFFSET
/* set the end flag */
movi a0, esp_ipc_isr_end_fl
s32i a0, a0, 0
/* restore a0 */
rsr a0, EXCSAVE_4
/* restores PS from EPS[4] and jumps to the address in EPC[4] */
rfi 4

View File

@@ -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

View File

@@ -1,4 +1,4 @@
if(IDF_TARGET STREQUAL "esp32")
if(IDF_TARGET STREQUAL "esp32" OR IDF_TARGET STREQUAL "esp32s3")
idf_component_register(SRC_DIRS "."
PRIV_INCLUDE_DIRS "."
PRIV_REQUIRES cmock test_utils esp_ipc)

View File

@@ -1 +1,2 @@
COMPONENT_SRCDIRS := .
COMPONENT_ADD_LDFLAGS = -Wl,--whole-archive -l$(COMPONENT_NAME) -Wl,--no-whole-archive

View 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

View File

@@ -0,0 +1,85 @@
#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 */