mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
feat(esp_system): Support IPC_ISR for ESP32P4
This commit is contained in:
parent
addfc0d870
commit
7a878bdc50
@ -530,19 +530,19 @@ menu "ESP System Settings"
|
||||
prompt "Interrupt level to use for Interrupt Watchdog and other system checks"
|
||||
default ESP_SYSTEM_CHECK_INT_LEVEL_4
|
||||
help
|
||||
Interrupt level to use for Interrupt Watchdog and other system checks.
|
||||
Interrupt level to use for Interrupt Watchdog, IPC_ISR and other system checks.
|
||||
|
||||
config ESP_SYSTEM_CHECK_INT_LEVEL_5
|
||||
bool "Level 5 interrupt"
|
||||
depends on IDF_TARGET_ESP32
|
||||
help
|
||||
Using level 5 interrupt for Interrupt Watchdog and other system checks.
|
||||
Using level 5 interrupt for Interrupt Watchdog, IPC_ISR and other system checks.
|
||||
|
||||
config ESP_SYSTEM_CHECK_INT_LEVEL_4
|
||||
bool "Level 4 interrupt"
|
||||
depends on !BTDM_CTRL_HLI
|
||||
help
|
||||
Using level 4 interrupt for Interrupt Watchdog and other system checks.
|
||||
Using level 4 interrupt for Interrupt Watchdog, IPC_ISR and other system checks.
|
||||
endchoice
|
||||
|
||||
# Insert chip-specific system config
|
||||
@ -595,11 +595,10 @@ menu "IPC (Inter-Processor Call)"
|
||||
|
||||
config ESP_IPC_ISR_ENABLE
|
||||
bool
|
||||
default n if IDF_TARGET_ESP32P4 # TODO: IDF-7769
|
||||
default y if !FREERTOS_UNICORE
|
||||
help
|
||||
The IPC ISR feature is similar to the IPC feature except that the callback function is executed in the
|
||||
context of a High Priority Interrupt. The IPC ISR feature is itended for low latency execution of simple
|
||||
context of a High Priority Interrupt. The IPC ISR feature is intended for low latency execution of simple
|
||||
callbacks written in assembly on another CPU. Due to being run in a High Priority Interrupt, the assembly
|
||||
callbacks must be written with particular restrictions (see "IPC" and "High-Level Interrupt" docs for more
|
||||
details).
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -17,31 +17,44 @@ extern "C" {
|
||||
/**
|
||||
* @brief IPC ISR Callback
|
||||
*
|
||||
* A callback of this type should be provided as an argument when calling esp_ipc_isr_asm_call() or
|
||||
* esp_ipc_isr_asm_call_blocking().
|
||||
* 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().
|
||||
*/
|
||||
typedef void (*esp_ipc_isr_func_t)(void* arg);
|
||||
|
||||
/**
|
||||
* @brief Execute an assembly callback on the other CPU
|
||||
* @brief 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, is invoked using a CALLX0 instruction, and has a2, a3, a4 as scratch
|
||||
* registers. See docs for more details
|
||||
* - 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).
|
||||
*
|
||||
* @note This function is not available in single-core mode.
|
||||
*
|
||||
* @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);
|
||||
void esp_ipc_isr_call(esp_ipc_isr_func_t func, void* arg) ;
|
||||
|
||||
/**
|
||||
* @brief Execute an assembly callback on the other CPU and busy-wait until it completes
|
||||
* @brief Execute an ISR callback on the other CPU
|
||||
* See esp_ipc_isr_call().
|
||||
*/
|
||||
#define esp_ipc_isr_asm_call(func, arg) esp_ipc_isr_call(func, arg)
|
||||
|
||||
/**
|
||||
* @brief Execute an ISR callback on the other CPU and busy-wait until it completes
|
||||
*
|
||||
* This function is identical to esp_ipc_isr_asm_call() except that this function will busy-wait until the execution of
|
||||
* This function is identical to esp_ipc_isr_call() except that this function will busy-wait until the execution of
|
||||
* the callback completes.
|
||||
*
|
||||
* @note This function is not available in single-core mode.
|
||||
@ -49,7 +62,13 @@ void esp_ipc_isr_asm_call(esp_ipc_isr_func_t func, void* arg);
|
||||
* @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);
|
||||
void esp_ipc_isr_call_blocking(esp_ipc_isr_func_t func, void* arg);
|
||||
|
||||
/**
|
||||
* @brief Execute an ISR callback on the other CPU and busy-wait until it completes
|
||||
* See esp_ipc_isr_call_blocking().
|
||||
*/
|
||||
#define esp_ipc_isr_asm_call_blocking(func, arg) esp_ipc_isr_call_blocking(func, arg)
|
||||
|
||||
/**
|
||||
* @brief Stall the other CPU
|
||||
|
@ -26,7 +26,7 @@ extern "C" {
|
||||
* - This function will register a High Priority Interrupt for a CPU where it is called. The priority of the interrupts is dependent on
|
||||
* the CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL option.
|
||||
* - Callbacks written in assembly can then run in context of the registered High Priority Interrupts
|
||||
* - Callbacks can be executed by calling esp_ipc_isr_asm_call() or esp_ipc_isr_asm_call_blocking()
|
||||
* - Callbacks can be executed by calling esp_ipc_isr_call() or esp_ipc_isr_call_blocking()
|
||||
*/
|
||||
void esp_ipc_isr_init(void);
|
||||
|
||||
|
@ -17,11 +17,16 @@ if(CONFIG_ESP_CONSOLE_USB_CDC)
|
||||
endif()
|
||||
|
||||
if(CONFIG_ESP_IPC_ISR_ENABLE)
|
||||
if(CONFIG_IDF_TARGET_ARCH_XTENSA)
|
||||
list(APPEND srcs "arch/xtensa/esp_ipc_isr.c"
|
||||
"arch/xtensa/esp_ipc_isr_handler.S"
|
||||
"arch/xtensa/esp_ipc_isr_routines.S")
|
||||
endif()
|
||||
list(APPEND srcs "esp_ipc_isr.c")
|
||||
if(CONFIG_IDF_TARGET_ARCH_XTENSA)
|
||||
list(APPEND srcs "arch/xtensa/esp_ipc_isr_port.c"
|
||||
"arch/xtensa/esp_ipc_isr_handler.S"
|
||||
"arch/xtensa/esp_ipc_isr_routines.S")
|
||||
elseif(CONFIG_IDF_TARGET_ARCH_RISCV)
|
||||
list(APPEND srcs "arch/riscv/esp_ipc_isr_port.c"
|
||||
"arch/riscv/esp_ipc_isr_handler.S"
|
||||
"arch/riscv/esp_ipc_isr_routines.c")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(CONFIG_IDF_TARGET_ARCH_XTENSA)
|
||||
|
78
components/esp_system/port/arch/riscv/esp_ipc_isr_handler.S
Normal file
78
components/esp_system/port/arch/riscv/esp_ipc_isr_handler.S
Normal file
@ -0,0 +1,78 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "soc/hp_system_reg.h"
|
||||
|
||||
/* IPC_ISR handler */
|
||||
.equ SAVE_REGS, 16 /* count of saving regs: a0 - a7, t0 - t6, ra */
|
||||
.equ CONTEXT_SIZE, (SAVE_REGS * 4)
|
||||
.section .iram1,"ax"
|
||||
.global esp_ipc_isr_handler
|
||||
.type esp_ipc_isr_handler,@function
|
||||
esp_ipc_isr_handler:
|
||||
|
||||
/* save a0 - a7, t0 - t6, ra */
|
||||
addi sp, sp, -(CONTEXT_SIZE)
|
||||
sw a0, 0(sp)
|
||||
sw a1, 4(sp)
|
||||
sw a2, 8(sp)
|
||||
sw a3, 12(sp)
|
||||
sw a4, 16(sp)
|
||||
sw a5, 20(sp)
|
||||
sw a6, 24(sp)
|
||||
sw a7, 28(sp)
|
||||
sw t0, 32(sp)
|
||||
sw t1, 36(sp)
|
||||
sw t2, 40(sp)
|
||||
sw t3, 44(sp)
|
||||
sw t4, 48(sp)
|
||||
sw t5, 52(sp)
|
||||
sw t6, 56(sp)
|
||||
sw ra, 60(sp)
|
||||
|
||||
/* MIE is cleared, so nested interrupts are disabled */
|
||||
|
||||
/* Reset isr interrupt flags */
|
||||
li a1, HP_SYSTEM_CPU_INT_FROM_CPU_2_REG
|
||||
csrr a0, mhartid # Get CORE_ID
|
||||
beqz a0, 1f
|
||||
li a1, HP_SYSTEM_CPU_INT_FROM_CPU_3_REG
|
||||
1:
|
||||
sw zero, (a1)
|
||||
|
||||
/* Set the start flag */
|
||||
la a0, esp_ipc_isr_start_fl
|
||||
sw a0, 0(a0)
|
||||
|
||||
/* Call the esp_ipc_func(void* esp_ipc_func_arg) */
|
||||
lw a1, (esp_ipc_func)
|
||||
lw a0, (esp_ipc_func_arg)
|
||||
jalr a1
|
||||
|
||||
/* Set the end flag */
|
||||
la a0, esp_ipc_isr_end_fl
|
||||
sw a0, 0(a0)
|
||||
|
||||
/* Restore a0 - a7, t0 - t6, ra */
|
||||
lw a0, 0(sp)
|
||||
lw a1, 4(sp)
|
||||
lw a2, 8(sp)
|
||||
lw a3, 12(sp)
|
||||
lw a4, 16(sp)
|
||||
lw a5, 20(sp)
|
||||
lw a6, 24(sp)
|
||||
lw a7, 28(sp)
|
||||
lw t0, 32(sp)
|
||||
lw t1, 36(sp)
|
||||
lw t2, 40(sp)
|
||||
lw t3, 44(sp)
|
||||
lw t4, 48(sp)
|
||||
lw t5, 52(sp)
|
||||
lw t6, 56(sp)
|
||||
lw ra, 60(sp)
|
||||
addi sp, sp, (CONTEXT_SIZE)
|
||||
|
||||
mret
|
48
components/esp_system/port/arch/riscv/esp_ipc_isr_port.c
Normal file
48
components/esp_system/port/arch/riscv/esp_ipc_isr_port.c
Normal file
@ -0,0 +1,48 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "soc/soc.h"
|
||||
#include "soc/interrupts.h"
|
||||
#include "soc/hp_system_reg.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "riscv/interrupt.h"
|
||||
#include "esp_rom_sys.h"
|
||||
#include "esp_cpu.h"
|
||||
#include "esp_attr.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
|
||||
void esp_ipc_isr_port_init(const int cpuid)
|
||||
{
|
||||
uint32_t intr_source = ETS_FROM_CPU_INTR2_SOURCE + cpuid; // ETS_FROM_CPU_INTR2_SOURCE and ETS_FROM_CPU_INTR3_SOURCE
|
||||
|
||||
esp_intr_disable_source(ETS_IPC_ISR_INUM);
|
||||
|
||||
esp_rom_route_intr_matrix(cpuid, intr_source, ETS_IPC_ISR_INUM);
|
||||
|
||||
esp_cpu_intr_set_type(ETS_IPC_ISR_INUM, INTR_TYPE_LEVEL);
|
||||
|
||||
#if CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_5
|
||||
esp_cpu_intr_set_priority(ETS_IPC_ISR_INUM, 5);
|
||||
#elif CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL_4
|
||||
esp_cpu_intr_set_priority(ETS_IPC_ISR_INUM, 4);
|
||||
#else
|
||||
#error "CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL is not defined!"
|
||||
#endif
|
||||
|
||||
esp_intr_enable_source(ETS_IPC_ISR_INUM);
|
||||
}
|
||||
|
||||
IRAM_ATTR void esp_ipc_isr_port_int_trigger(const int cpuid)
|
||||
{
|
||||
if (cpuid == 0) {
|
||||
// it runs an interrupt on cpu0
|
||||
REG_WRITE(HP_SYSTEM_CPU_INT_FROM_CPU_2_REG, HP_SYSTEM_CPU_INT_FROM_CPU_2);
|
||||
} else {
|
||||
// it runs an interrupt on cpu1
|
||||
REG_WRITE(HP_SYSTEM_CPU_INT_FROM_CPU_3_REG, HP_SYSTEM_CPU_INT_FROM_CPU_3);
|
||||
}
|
||||
}
|
12
components/esp_system/port/arch/riscv/esp_ipc_isr_routines.c
Normal file
12
components/esp_system/port/arch/riscv/esp_ipc_isr_routines.c
Normal file
@ -0,0 +1,12 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "stdint.h"
|
||||
|
||||
void esp_ipc_isr_waiting_for_finish_cmd(void* ipc_isr_finish_cmd)
|
||||
{
|
||||
while (*(volatile uint32_t*)ipc_isr_finish_cmd == 0) { };
|
||||
}
|
36
components/esp_system/port/arch/xtensa/esp_ipc_isr_port.c
Normal file
36
components/esp_system/port/arch/xtensa/esp_ipc_isr_port.c
Normal file
@ -0,0 +1,36 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include "soc/soc.h"
|
||||
#include "soc/interrupts.h"
|
||||
#include "soc/dport_reg.h"
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32
|
||||
#include "soc/system_reg.h"
|
||||
#endif
|
||||
#include "esp_rom_sys.h"
|
||||
#include "esp_intr_alloc.h"
|
||||
#include "esp_attr.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
|
||||
void esp_ipc_isr_port_init(const int cpuid)
|
||||
{
|
||||
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);
|
||||
esp_rom_route_intr_matrix(cpuid, intr_source, ETS_IPC_ISR_INUM);
|
||||
ESP_INTR_ENABLE(ETS_IPC_ISR_INUM);
|
||||
}
|
||||
|
||||
IRAM_ATTR void esp_ipc_isr_port_int_trigger(const int cpuid)
|
||||
{
|
||||
if (cpuid == 0) {
|
||||
// it runs an interrupt on cpu0
|
||||
DPORT_REG_WRITE(SYSTEM_CPU_INTR_FROM_CPU_2_REG, SYSTEM_CPU_INTR_FROM_CPU_2);
|
||||
} else {
|
||||
// it runs an interrupt on cpu1
|
||||
DPORT_REG_WRITE(SYSTEM_CPU_INTR_FROM_CPU_3_REG, SYSTEM_CPU_INTR_FROM_CPU_3);
|
||||
}
|
||||
}
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2017-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2017-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -10,19 +10,12 @@
|
||||
#include <assert.h>
|
||||
#include "esp_err.h"
|
||||
#include "esp_attr.h"
|
||||
#include "soc/soc.h"
|
||||
#include "soc/dport_reg.h"
|
||||
#ifndef CONFIG_IDF_TARGET_ESP32
|
||||
#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_private/esp_ipc_isr.h"
|
||||
#include "esp_private/esp_ipc_isr_port.h"
|
||||
#include "esp_ipc_isr.h"
|
||||
#include "xtensa/core-macros.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
static portMUX_TYPE s_ipc_isr_mux = portMUX_INITIALIZER_UNLOCKED;
|
||||
@ -61,10 +54,7 @@ static void esp_ipc_isr_call_and_wait(esp_ipc_isr_func_t func, void* arg, esp_ip
|
||||
void esp_ipc_isr_init(void)
|
||||
{
|
||||
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);
|
||||
esp_rom_route_intr_matrix(cpuid, intr_source, ETS_IPC_ISR_INUM);
|
||||
ESP_INTR_ENABLE(ETS_IPC_ISR_INUM);
|
||||
esp_ipc_isr_port_init(cpuid);
|
||||
|
||||
if (cpuid != 0) {
|
||||
s_stall_state = STALL_STATE_RUNNING;
|
||||
@ -76,14 +66,14 @@ void esp_ipc_isr_init(void)
|
||||
|
||||
/* Public API functions */
|
||||
|
||||
void IRAM_ATTR esp_ipc_isr_asm_call(esp_ipc_isr_func_t func, void* arg)
|
||||
void IRAM_ATTR esp_ipc_isr_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)
|
||||
void IRAM_ATTR esp_ipc_isr_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);
|
||||
@ -201,14 +191,8 @@ static void IRAM_ATTR esp_ipc_isr_call_and_wait(esp_ipc_isr_func_t func, void* a
|
||||
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);
|
||||
}
|
||||
|
||||
// Trigger an interrupt on the opposite core.
|
||||
esp_ipc_isr_port_int_trigger(!cpu_id);
|
||||
// IPC_ISR handler will be called and `...isr_start` and `...isr_end` will be updated there
|
||||
|
||||
if (wait_for == IPC_ISR_WAIT_FOR_START) {
|
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 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
|
||||
|
||||
/**
|
||||
* @brief Initialize the IPC ISR interrupt for a specific CPU.
|
||||
*
|
||||
* This function initializes the IPC ISR (Inter-Processor Communication Interrupt)
|
||||
* for a specific CPU core. It configures the interrupt source and enables the
|
||||
* IPC ISR interrupt for the specified CPU.
|
||||
*
|
||||
* @param[in] cpuid The ID of the CPU core to initialize IPC ISR for.
|
||||
*/
|
||||
void esp_ipc_isr_port_init(const int cpuid);
|
||||
|
||||
/**
|
||||
* @brief Trigger an interrupt on a specific CPU core.
|
||||
*
|
||||
* @param[in] cpuid The ID of the CPU core to trigger the interrupt on (0 or 1).
|
||||
*/
|
||||
void esp_ipc_isr_port_int_trigger(const int cpuid);
|
||||
|
||||
#endif // CONFIG_ESP_IPC_ISR_ENABLE
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
@ -16,7 +16,12 @@ set(SRC "test_app_main.c"
|
||||
"test_task_wdt.c")
|
||||
|
||||
if(CONFIG_ESP_IPC_ISR_ENABLE)
|
||||
list(APPEND SRC "test_ipc_isr.c" "test_ipc_isr.S")
|
||||
list(APPEND SRC "test_ipc_isr.c")
|
||||
if(CONFIG_IDF_TARGET_ARCH_XTENSA)
|
||||
list(APPEND SRC "port/arch/xtensa/test_ipc_isr.S")
|
||||
elseif(CONFIG_IDF_TARGET_ARCH_RISCV)
|
||||
list(APPEND SRC "port/arch/riscv/test_ipc_isr.c")
|
||||
endif()
|
||||
endif()
|
||||
|
||||
idf_component_register(SRCS ${SRC}
|
||||
|
@ -0,0 +1,29 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if CONFIG_ESP_IPC_ISR_ENABLE
|
||||
|
||||
void esp_test_ipc_isr_callback(void *arg) {
|
||||
uint32_t value = 0xa5a5;
|
||||
*(volatile uint32_t*)arg = value;
|
||||
}
|
||||
|
||||
void esp_test_ipc_isr_get_other_core_id(void *arg) {
|
||||
uint32_t core_id;
|
||||
__asm volatile("csrr %0, mhartid" : "=r"(core_id));
|
||||
*(volatile uint32_t*)arg = core_id;
|
||||
}
|
||||
|
||||
void esp_test_ipc_isr_get_cycle_count_other_cpu(void *arg) {
|
||||
uint32_t cycle_count;
|
||||
__asm volatile("rdcycle %0;" : "=r"(cycle_count));
|
||||
*(volatile uint32_t*)arg = cycle_count;
|
||||
}
|
||||
|
||||
#endif // CONFIG_ESP_IPC_ISR_ENABLE
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2021 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -13,18 +13,18 @@
|
||||
#include <xtensa/config/system.h>
|
||||
#include <xtensa/hal.h>
|
||||
|
||||
/* esp_test_ipc_isr_asm(void *arg)
|
||||
/* esp_test_ipc_isr_callback(void *arg)
|
||||
*
|
||||
* It should be called by the CALLX0 command from the handler of High-priority interrupt.
|
||||
* 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
|
||||
.global esp_test_ipc_isr_callback
|
||||
.type esp_test_ipc_isr_callback, @function
|
||||
// Args:
|
||||
// a2 - void* arg
|
||||
esp_test_ipc_isr_asm:
|
||||
esp_test_ipc_isr_callback:
|
||||
movi a3, 0xa5a5
|
||||
s32i a3, a2, 0
|
||||
ret
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -17,12 +17,18 @@
|
||||
|
||||
#ifdef CONFIG_ESP_IPC_ISR_ENABLE
|
||||
|
||||
void esp_test_ipc_isr_asm(void* arg);
|
||||
#if CONFIG_IDF_TARGET_ARCH_RISCV
|
||||
#define STORE_ERROR "Store access fault"
|
||||
#else
|
||||
#define STORE_ERROR "StoreProhibited"
|
||||
#endif
|
||||
|
||||
void esp_test_ipc_isr_callback(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);
|
||||
esp_ipc_isr_call_blocking(esp_test_ipc_isr_callback, &val);
|
||||
TEST_ASSERT_EQUAL_HEX(val, 0xa5a5);
|
||||
}
|
||||
|
||||
@ -32,13 +38,13 @@ 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);
|
||||
esp_ipc_isr_call_blocking(esp_test_ipc_isr_get_other_core_id, &val);
|
||||
TEST_ASSERT_EQUAL_HEX(val, 1);
|
||||
}
|
||||
|
||||
static void do_esp_ipc_isr_asm_call_blocking(void)
|
||||
static void do_esp_ipc_isr_call_blocking(void)
|
||||
{
|
||||
esp_ipc_isr_asm_call_blocking(esp_test_ipc_isr_asm, NULL);
|
||||
esp_ipc_isr_call_blocking(esp_test_ipc_isr_callback, NULL);
|
||||
}
|
||||
|
||||
static void check_reset_reason_panic(void)
|
||||
@ -46,8 +52,8 @@ static void check_reset_reason_panic(void)
|
||||
TEST_ASSERT_EQUAL(ESP_RST_PANIC, esp_reset_reason());
|
||||
}
|
||||
|
||||
TEST_CASE_MULTIPLE_STAGES("Test ipc_isr exception in asm func leads to StoreProhibited not to Unhandled debug exception", "[ipc][reset=StoreProhibited,SW_CPU_RESET]",
|
||||
do_esp_ipc_isr_asm_call_blocking,
|
||||
TEST_CASE_MULTIPLE_STAGES("Test ipc_isr exception in asm func leads to StoreProhibited not to Unhandled debug exception", "[ipc][reset="STORE_ERROR",SW_CPU_RESET]",
|
||||
do_esp_ipc_isr_call_blocking,
|
||||
check_reset_reason_panic)
|
||||
|
||||
void esp_test_ipc_isr_get_cycle_count_other_cpu(void* arg);
|
||||
@ -55,7 +61,7 @@ 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_ipc_isr_call_blocking(esp_test_ipc_isr_get_cycle_count_other_cpu, &val);
|
||||
esp_rom_printf("CCOUNT CPU0 = %d\n", esp_cpu_get_cycle_count());
|
||||
esp_rom_printf("CCOUNT CPU1 = %d\n", val);
|
||||
}
|
||||
@ -70,12 +76,12 @@ static void task_asm(void *arg)
|
||||
printf("task_asm\n");
|
||||
while (s_stop == false) {
|
||||
val = 0x5a5a;
|
||||
esp_ipc_isr_asm_call_blocking(esp_test_ipc_isr_asm, &val);
|
||||
esp_ipc_isr_call_blocking(esp_test_ipc_isr_callback, &val);
|
||||
TEST_ASSERT_EQUAL_HEX(val, 0xa5a5);
|
||||
++counter;
|
||||
}
|
||||
printf("task_asm counter = %d\n", counter);
|
||||
TEST_ASSERT_GREATER_THAN(10000, counter);
|
||||
TEST_ASSERT_GREATER_THAN(1000, counter);
|
||||
xSemaphoreGive(*sema);
|
||||
vTaskDelete(NULL);
|
||||
}
|
||||
|
@ -14,6 +14,12 @@
|
||||
#define MEMPROT_ISR _interrupt_handler
|
||||
#endif // CONFIG_ESP_SYSTEM_MEMPROT_FEATURE
|
||||
|
||||
#if CONFIG_ESP_IPC_ISR_ENABLE
|
||||
#define IPC_ISR_HANDLER esp_ipc_isr_handler
|
||||
#else
|
||||
#define IPC_ISR_HANDLER _interrupt_handler
|
||||
#endif // CONFIG_ESP_IPC_ISR_ENABLE
|
||||
|
||||
/* The system interrupts are not used for now, so trigger a panic every time one occurs. */
|
||||
#define _system_int_handler _panic_handler
|
||||
|
||||
@ -104,7 +110,7 @@ _mtvt_table:
|
||||
.word _panic_handler /* 41: ETS_CACHEERR_INUM (+16) panic-interrupt (soc-level panic) */
|
||||
.word MEMPROT_ISR /* 42: ETS_MEMPROT_ERR_INUM (+16) handler (soc-level panic) */
|
||||
.word _interrupt_handler /* 43: Free interrupt number */
|
||||
.word _interrupt_handler /* 44: Free interrupt number */
|
||||
.word IPC_ISR_HANDLER /* 44: ETS_IPC_ISR_INUM (+16) handler*/
|
||||
.word _interrupt_handler /* 45: Free interrupt number */
|
||||
.word _interrupt_handler /* 46: Free interrupt number */
|
||||
.word _interrupt_handler /* 47: Free interrupt number */
|
||||
|
@ -5,7 +5,6 @@
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include "soc/soc.h"
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -234,6 +234,7 @@
|
||||
#define ETS_T1_WDT_INUM 24
|
||||
#define ETS_CACHEERR_INUM 25
|
||||
#define ETS_MEMPROT_ERR_INUM 26
|
||||
#define ETS_IPC_ISR_INUM 28
|
||||
//CPU0 Max valid interrupt number
|
||||
#define ETS_MAX_INUM 31
|
||||
|
||||
|
@ -183,7 +183,6 @@ api-reference/system/bootloader_image_format.rst
|
||||
api-reference/system/inc/power_management_esp32p4.rst
|
||||
api-reference/system/heap_debug.rst
|
||||
api-reference/system/mm.rst
|
||||
api-reference/system/ipc.rst
|
||||
api-reference/system/esp_https_ota.rst
|
||||
api-reference/system/ulp-risc-v.rst
|
||||
api-reference/system/esp_err.rst
|
||||
|
@ -24,7 +24,7 @@ System API
|
||||
heap_debug
|
||||
esp_timer
|
||||
internal-unstable
|
||||
:not CONFIG_FREERTOS_UNICORE or esp32p4: ipc
|
||||
:not CONFIG_FREERTOS_UNICORE: ipc
|
||||
intr_alloc
|
||||
log
|
||||
misc_system_api
|
||||
|
@ -14,11 +14,7 @@ Due to the dual core nature of the {IDF_TARGET_NAME}, there are instances where
|
||||
- On particular chips (such as the ESP32), accessing memory that is exclusive to a particular CPU (such as RTC Fast Memory).
|
||||
- Reading the registers/state of another CPU.
|
||||
|
||||
|
||||
.. only:: not esp32p4
|
||||
|
||||
The IPC (Inter-Processor Call) feature allows a particular CPU (the calling CPU) to trigger the execution of a callback function on another CPU (the target CPU). The IPC feature allows execution of a callback function on the target CPU in either a task context, or a High Priority Interrupt context (see :doc:`/api-guides/hlinterrupts` for more details). Depending on the context that the callback function is executed in, different restrictions apply to the implementation of the callback function.
|
||||
|
||||
The IPC (Inter-Processor Call) feature allows a particular CPU (the calling CPU) to trigger the execution of a callback function on another CPU (the target CPU). The IPC feature allows execution of a callback function on the target CPU in either ``a task context``, or ``an interrupt context``. Depending on the context that the callback function is executed in, different restrictions apply to the implementation of the callback function.
|
||||
|
||||
IPC in Task Context
|
||||
-------------------
|
||||
@ -46,76 +42,103 @@ The IPC feature offers the API listed below to execute a callback in a task cont
|
||||
- :cpp:func:`esp_ipc_call` triggers an IPC call on the target CPU. This function will block until the target CPU's IPC task **begins** execution of the callback.
|
||||
- :cpp:func:`esp_ipc_call_blocking` triggers an IPC on the target CPU. This function will block until the target CPU's IPC task **completes** execution of the callback.
|
||||
|
||||
.. only:: not esp32p4
|
||||
IPC in Interrupt Context
|
||||
------------------------
|
||||
|
||||
IPC in ISR Context
|
||||
------------------
|
||||
In some cases, we need to quickly obtain the state of another CPU such as in a core dump, GDB stub, various unit tests, and DPORT workaround. The IPC ISR feature implements the High Priority Interrupt context by reserving a High Priority Interrupt on each CPU for IPC usage. When a calling CPU needs to execute a callback on the target CPU, the callback will execute in the context of the High Priority Interrupt of the target CPU.
|
||||
|
||||
In some cases, we need to quickly obtain the state of another CPU such as in a core dump, GDB stub, various unit tests, and DPORT workaround. For such scenarios, the IPC feature supports execution of callbacks in a :doc:`High Priority Interrupt </api-guides/hlinterrupts>` context. The IPC feature implements the High Priority Interrupt context by reserving a High Priority Interrupt on each CPU for IPC usage. When a calling CPU needs to execute a callback on the target CPU, the callback will execute in the context of the High Priority Interrupt of the target CPU.
|
||||
.. only:: CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||
|
||||
When using IPCs in High Priority Interrupt context, users need to consider the following:
|
||||
For such scenarios, the IPC ISR feature supports execution of callbacks in a :doc:`High Priority Interrupt </api-guides/hlinterrupts>` context.
|
||||
|
||||
- Since the callback is executed in a High Priority Interrupt context, the callback must be written entirely in assembly. See the API Usage below for more details regarding writing assembly callbacks.
|
||||
- The priority of the reserved High Priority Interrupt is dependent on the :ref:`CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL` option
|
||||
- When the callback executes:
|
||||
When using IPCs in High Priority Interrupt context, users need to consider the following:
|
||||
|
||||
- The calling CPU will disable interrupts of level 3 and lower
|
||||
- Although the priority of the reserved interrupt depends on :ref:`CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL`, during the execution IPC ISR callback, the target CPU will disable interrupts of level 5 and lower regardless of what :ref:`CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL` is set to.
|
||||
.. list::
|
||||
|
||||
:CONFIG_IDF_TARGET_ARCH_XTENSA: - Since the callback is executed in a High Priority Interrupt context, the callback must be written entirely in assembly. See the API Usage below for more details regarding writing assembly callbacks.
|
||||
- The priority of the reserved High Priority Interrupt is dependent on the :ref:`CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL` option.
|
||||
|
||||
When the callback executes, users need to consider the following:
|
||||
|
||||
.. list::
|
||||
|
||||
- The calling CPU will disable interrupts of level 3 and lower.
|
||||
:CONFIG_IDF_TARGET_ARCH_XTENSA: - Although the priority of the reserved interrupt depends on :ref:`CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL`, during the execution IPC ISR callback, the target CPU will disable interrupts of level 5 and lower regardless of what :ref:`CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL` is set to.
|
||||
:CONFIG_IDF_TARGET_ARCH_RISCV: - Although the priority of the reserved interrupt depends on :ref:`CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL`, during the execution IPC ISR callback, the target CPU will disable all interrupts.
|
||||
|
||||
API Usage
|
||||
^^^^^^^^^
|
||||
|
||||
High Priority Interrupt IPC callbacks have the following restrictions:
|
||||
.. only:: CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||
|
||||
- The callback must be of type ``void func(void *arg)`` but implemented entirely in assembly
|
||||
- The callback is invoked via the ``CALLX0`` instruction with register windowing disabled, thus the callback:
|
||||
- Must not call any register window related instructions (e.g., ``entry`` and ``retw``).
|
||||
- Must not call other C functions as register windowing is disabled
|
||||
- The callback should be placed in IRAM at a 4-byte aligned address
|
||||
- (On invocation of/after returning from) the callback, the registers ``a2, a3, a4`` are (saved/restored) automatically thus can be used in the callback. The callback should **ONLY** use those registers.
|
||||
- ``a2`` contains the ``void *arg`` of the callback
|
||||
- ``a3/a4`` are free to use as scratch registers
|
||||
High Priority Interrupt IPC callbacks have the following restrictions:
|
||||
|
||||
- The callback must be of type ``void func(void *arg)`` but implemented entirely in assembly
|
||||
- The callback is invoked via the ``CALLX0`` instruction with register windowing disabled, thus the callback:
|
||||
- Must not call any register window related instructions (e.g., ``entry`` and ``retw``).
|
||||
- Must not call other C functions as register windowing is disabled
|
||||
- The callback should be placed in IRAM at a 4-byte aligned address
|
||||
- (On invocation of/after returning from) the callback, the registers ``a2, a3, a4`` are (saved/restored) automatically thus can be used in the callback. The callback should **ONLY** use those registers.
|
||||
- ``a2`` contains the ``void *arg`` of the callback
|
||||
- ``a3/a4`` are free to use as scratch registers
|
||||
|
||||
.. only:: CONFIG_IDF_TARGET_ARCH_RISCV
|
||||
|
||||
High Priority Interrupt IPC callbacks have the same restrictions as for regular interrupt handlers. The callback function can be written in C.
|
||||
|
||||
The IPC feature offers the API listed below to execute a callback in a High Priority Interrupt context.
|
||||
|
||||
- :cpp:func:`esp_ipc_isr_asm_call` triggers an IPC call on the target CPU. This function will busy-wait until the target CPU begins execution of the callback.
|
||||
- :cpp:func:`esp_ipc_isr_asm_call_blocking` triggers an IPC call on the target CPU. This function will busy-wait until the target CPU completes execution of the callback.
|
||||
- :cpp:func:`esp_ipc_isr_call` triggers an IPC call on the target CPU. This function will busy-wait until the target CPU begins execution of the callback.
|
||||
- :cpp:func:`esp_ipc_isr_call_blocking` triggers an IPC call on the target CPU. This function will busy-wait until the target CPU completes execution of the callback.
|
||||
|
||||
The following code-blocks demonstrates a High Priority Interrupt IPC callback written in assembly that simply reads the target CPU's cycle count.
|
||||
.. only:: CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||
|
||||
.. code-block:: asm
|
||||
The following code-blocks demonstrates a High Priority Interrupt IPC callback written in assembly that simply reads the target CPU's cycle count.
|
||||
|
||||
/* esp_test_ipc_isr_get_cycle_count_other_cpu(void *arg) */
|
||||
// this function reads CCOUNT of the target CPU and stores it in 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
|
||||
.. code-block:: asm
|
||||
|
||||
.. code-block:: c
|
||||
/* esp_test_ipc_isr_get_cycle_count_other_cpu(void *arg) */
|
||||
// this function reads CCOUNT of the target CPU and stores it in 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
|
||||
|
||||
unit32_t cycle_count;
|
||||
esp_ipc_isr_asm_call_blocking(esp_test_ipc_isr_get_cycle_count_other_cpu, (void *)cycle_count);
|
||||
.. code-block:: c
|
||||
|
||||
.. note::
|
||||
unit32_t cycle_count;
|
||||
esp_ipc_isr_call_blocking(esp_test_ipc_isr_get_cycle_count_other_cpu, (void *)cycle_count);
|
||||
|
||||
The number of scratch registers available for use is sufficient for most simple use cases. But if your callback requires more scratch registers, ``void *arg`` can point to a buffer that is used as a register save area. The callback can then save and restore more registers. See the :example:`system/ipc/ipc_isr`.
|
||||
.. note::
|
||||
|
||||
.. note::
|
||||
The number of scratch registers available for use is sufficient for most simple use cases. But if your callback requires more scratch registers, ``void *arg`` can point to a buffer that is used as a register save area. The callback can then save and restore more registers. See the :example:`system/ipc/ipc_isr`.
|
||||
|
||||
For more examples of High Priority Interrupt IPC callbacks, see :idf_file:`components/esp_system/port/arch/xtensa/esp_ipc_isr_routines.S` and :`components/esp_system/test/test_ipc_isr.S`
|
||||
.. note::
|
||||
|
||||
For more examples of High Priority Interrupt IPC callbacks, see :idf_file:`components/esp_system/port/arch/xtensa/esp_ipc_isr_routines.S` and :idf_file:`components/esp_system/test_apps/esp_system_unity_tests/main/port/arch/xtensa/test_ipc_isr.S`.
|
||||
|
||||
.. only:: CONFIG_IDF_TARGET_ARCH_RISCV
|
||||
|
||||
See :idf_file:`examples/system/ipc/ipc_isr/riscv/main/main.c` for an example of its use.
|
||||
|
||||
.. only:: CONFIG_IDF_TARGET_ARCH_XTENSA
|
||||
|
||||
See :idf_file:`examples/system/ipc/ipc_isr/xtensa/main/main.c` for an example of its use.
|
||||
|
||||
The High Priority Interrupt IPC API also provides the following convenience functions that can stall/resume the target CPU. These API utilize the High Priority Interrupt IPC, but supply their own internal callbacks:
|
||||
|
||||
- :cpp:func:`esp_ipc_isr_stall_other_cpu` stalls the target CPU. The calling CPU disables interrupts of level 3 and lower while the target CPU will busy-wait with interrupts of level 5 and lower disabled. The target CPU will busy-wait until :cpp:func:`esp_ipc_isr_release_other_cpu` is called.
|
||||
- :cpp:func:`esp_ipc_isr_release_other_cpu` resumes the target CPU.
|
||||
.. list::
|
||||
|
||||
:CONFIG_IDF_TARGET_ARCH_RISCV: - :cpp:func:`esp_ipc_isr_stall_other_cpu` stalls the target CPU. The calling CPU disables interrupts of level 3 and lower while the target CPU will busy-wait with all interrupts disabled. The target CPU will busy-wait until :cpp:func:`esp_ipc_isr_release_other_cpu` is called.
|
||||
:CONFIG_IDF_TARGET_ARCH_XTENSA: - :cpp:func:`esp_ipc_isr_stall_other_cpu` stalls the target CPU. The calling CPU disables interrupts of level 3 and lower while the target CPU will busy-wait with interrupts of level 5 and lower disabled. The target CPU will busy-wait until :cpp:func:`esp_ipc_isr_release_other_cpu` is called.
|
||||
- :cpp:func:`esp_ipc_isr_release_other_cpu` resumes the target CPU.
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
@ -80,11 +80,21 @@ examples/system/himem:
|
||||
temporary: true
|
||||
reason: the other targets are not tested yet
|
||||
|
||||
examples/system/ipc/ipc_isr:
|
||||
examples/system/ipc/ipc_isr/riscv:
|
||||
enable:
|
||||
- if: IDF_TARGET == "esp32" or IDF_TARGET == "esp32s3"
|
||||
- if: IDF_TARGET_ARCH_RISCV == 1 and ESP_IPC_ISR_ENABLE == 1
|
||||
temporary: true
|
||||
reason: the other targets are not tested yet
|
||||
reason: The test is intended only for multi-core chips
|
||||
disable_test:
|
||||
- if: IDF_TARGET == "esp32p4"
|
||||
temporary: true
|
||||
reason: lack of runners
|
||||
|
||||
examples/system/ipc/ipc_isr/xtensa:
|
||||
enable:
|
||||
- if: IDF_TARGET_ARCH_XTENSA == 1 and ESP_IPC_ISR_ENABLE == 1
|
||||
temporary: true
|
||||
reason: The test is intended only for multi-core chips
|
||||
|
||||
examples/system/light_sleep:
|
||||
disable:
|
||||
|
56
examples/system/ipc/ipc_isr/riscv/README.md
Normal file
56
examples/system/ipc/ipc_isr/riscv/README.md
Normal file
@ -0,0 +1,56 @@
|
||||
| Supported Targets | ESP32P4 |
|
||||
| ----------------- | ------- |
|
||||
|
||||
# IPC ISR Example
|
||||
|
||||
This example demonstrates how to use the IPC ISR feature (which allows an IPC to run in the context of a High Priority Interrupt). The level of the IPC ISR interrupt depends on the `CONFIG_ESP_SYSTEM_CHECK_INT_LEVEL` option. The IPC ISR feature can be useful in cases where users need to quickly get the state of the other CPU (consult the [IPC documentation](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/ipc.html)). Unlike ESP32 or ESP32S3, for this chip, the callback function can be written in C.
|
||||
|
||||
The first callback `get_mstatus_other_cpu()` demonstrates a callback that simply returns the `MSTATUS` register of other core.
|
||||
|
||||
The second callback `extended_ipc_isr_func()` demonstrates how to return multiple values from the callback. The callback's `void *arg` points to a structure that contains the following:
|
||||
- `uint32_t in[];` that gives the callback multiple input arguments
|
||||
- `uint32_t out[];` that gives the callback multiple output arguments
|
||||
|
||||
The `extended_ipc_isr_func()` callback uses the `in[]` arguments does some work and then writes to `out[]`.
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
Example should be able to run on any commonly available ESP32P4 development board. The chip should have two cores.
|
||||
|
||||
### Configure the project
|
||||
|
||||
- `CONFIG_FREERTOS_UNICORE` - disabled,
|
||||
- `CONFIG_ESP_IPC_ISR_ENABLE` - enabled.
|
||||
|
||||
```
|
||||
idf.py menuconfig
|
||||
```
|
||||
|
||||
### Build and Flash
|
||||
|
||||
```
|
||||
idf.py build flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
## Example output
|
||||
|
||||
```
|
||||
I (411) example: Start
|
||||
I (421) example: call get_mstatus_other_cpu
|
||||
I (421) example: MSTATUS = 0x3880
|
||||
I (421) example: call extended_ipc_isr_func
|
||||
I (431) example: in[0] = 0x1
|
||||
I (431) example: in[1] = 0x2
|
||||
I (441) example: in[2] = 0x3
|
||||
I (441) example: out[0] = (in[0] | in[1] | in[2]) = 0x3
|
||||
I (451) example: out[1] = (in[0] + in[1] + in[2]) = 0x6
|
||||
I (451) example: out[2] = MCAUSE of other cpu = 0xb800002c
|
||||
I (461) example: out[3] = MSTATUS of other cpu = 0x3880
|
||||
I (461) example: End
|
||||
```
|
3
examples/system/ipc/ipc_isr/riscv/main/CMakeLists.txt
Normal file
3
examples/system/ipc/ipc_isr/riscv/main/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "main.c"
|
||||
"callbacks.c"
|
||||
INCLUDE_DIRS ".")
|
22
examples/system/ipc/ipc_isr/riscv/main/callbacks.c
Normal file
22
examples/system/ipc/ipc_isr/riscv/main/callbacks.c
Normal file
@ -0,0 +1,22 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
#include "callbacks.h"
|
||||
|
||||
void get_mstatus_other_cpu(void *arg) {
|
||||
uint32_t mstatus_value;
|
||||
asm volatile ("csrr %0, mstatus" : "=r" (mstatus_value));
|
||||
*(volatile uint32_t*)arg = mstatus_value;
|
||||
}
|
||||
|
||||
void extended_ipc_isr_func(void* arg) {
|
||||
arg_data_t *a = (arg_data_t *)arg;
|
||||
a->out[0] = a->in[0] | a->in[1] | a->in[2];
|
||||
a->out[1] = a->in[0] + a->in[1] + a->in[2];
|
||||
asm volatile ("csrr %0, mcause" : "=r" (a->out[2]));
|
||||
asm volatile ("csrr %0, mstatus" : "=r" (a->out[3]));
|
||||
}
|
15
examples/system/ipc/ipc_isr/riscv/main/callbacks.h
Normal file
15
examples/system/ipc/ipc_isr/riscv/main/callbacks.h
Normal file
@ -0,0 +1,15 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
typedef struct {
|
||||
uint32_t in[3];
|
||||
uint32_t out[4];
|
||||
} arg_data_t;
|
||||
|
||||
void get_mstatus_other_cpu(void *arg);
|
||||
void extended_ipc_isr_func(void* arg);
|
43
examples/system/ipc/ipc_isr/riscv/main/main.c
Normal file
43
examples/system/ipc/ipc_isr/riscv/main/main.c
Normal file
@ -0,0 +1,43 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include "esp_log.h"
|
||||
#include "esp_ipc_isr.h"
|
||||
#include "callbacks.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
static const char* TAG = "example";
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
ESP_LOGI(TAG, "Start");
|
||||
uint32_t mstatus_other_cpu = 0;
|
||||
ESP_LOGI(TAG, "call get_mstatus_other_cpu");
|
||||
esp_ipc_isr_call_blocking(get_mstatus_other_cpu, &mstatus_other_cpu);
|
||||
ESP_LOGI(TAG, "MSTATUS = 0x%"PRIx32, mstatus_other_cpu);
|
||||
|
||||
ESP_LOGI(TAG, "call extended_ipc_isr_func");
|
||||
arg_data_t arg = { 0 };
|
||||
arg.in[0] = 0x01;
|
||||
arg.in[1] = 0x02;
|
||||
arg.in[2] = 0x03;
|
||||
ESP_LOGI(TAG, "in[0] = 0x%"PRIx32, arg.in[0]);
|
||||
ESP_LOGI(TAG, "in[1] = 0x%"PRIx32, arg.in[1]);
|
||||
ESP_LOGI(TAG, "in[2] = 0x%"PRIx32, arg.in[2]);
|
||||
esp_ipc_isr_call_blocking(extended_ipc_isr_func, (void*)&arg);
|
||||
ESP_LOGI(TAG, "out[0] = (in[0] | in[1] | in[2]) = 0x%"PRIx32, arg.out[0]);
|
||||
assert(0x03 == arg.out[0]);
|
||||
ESP_LOGI(TAG, "out[1] = (in[0] + in[1] + in[2]) = 0x%"PRIx32, arg.out[1]);
|
||||
assert(0x06 == arg.out[1]);
|
||||
ESP_LOGI(TAG, "out[2] = MCAUSE of other cpu = 0x%"PRIx32, arg.out[2]);
|
||||
assert(0xb800002c == arg.out[2]);
|
||||
ESP_LOGI(TAG, "out[3] = MSTATUS of other cpu = 0x%"PRIx32, arg.out[3]);
|
||||
assert(mstatus_other_cpu == arg.out[3]);
|
||||
ESP_LOGI(TAG, "End");
|
||||
}
|
20
examples/system/ipc/ipc_isr/riscv/pytest_ipc_isr_riscv.py
Normal file
20
examples/system/ipc/ipc_isr/riscv/pytest_ipc_isr_riscv.py
Normal file
@ -0,0 +1,20 @@
|
||||
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
|
||||
|
||||
@pytest.mark.esp32p4
|
||||
@pytest.mark.generic
|
||||
def test_ipc_isr(dut: Dut) -> None:
|
||||
dut.expect_exact('example: Start')
|
||||
dut.expect_exact('example: MSTATUS = 0x3880')
|
||||
dut.expect_exact('example: in[0] = 0x1')
|
||||
dut.expect_exact('example: in[1] = 0x2')
|
||||
dut.expect_exact('example: in[2] = 0x3')
|
||||
dut.expect_exact('example: out[0] = (in[0] | in[1] | in[2]) = 0x3')
|
||||
dut.expect_exact('example: out[1] = (in[0] + in[1] + in[2]) = 0x6')
|
||||
dut.expect_exact('example: out[2] = MCAUSE of other cpu = 0xb800002c')
|
||||
dut.expect_exact('example: out[3] = MSTATUS of other cpu = 0x3880')
|
||||
dut.expect_exact('example: End')
|
6
examples/system/ipc/ipc_isr/xtensa/CMakeLists.txt
Normal file
6
examples/system/ipc/ipc_isr/xtensa/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(ipc_isr)
|
@ -53,7 +53,7 @@ I (324) example: in[0] = 0x1
|
||||
I (334) example: in[1] = 0x2
|
||||
I (334) example: in[2] = 0x3
|
||||
I (334) example: out[0] = (in[0] | in[1] | in[2]) = 0x3
|
||||
I (344) example: out[1] = (in[0] & in[1] & in[2]) = 0x6
|
||||
I (344) example: out[1] = (in[0] + in[1] + in[2]) = 0x6
|
||||
I (354) example: out[2] = in[2] = 0x3
|
||||
I (354) example: out[3] = PS of other cpu = 0x25
|
||||
I (364) example: End
|
@ -1,16 +1,12 @@
|
||||
/* ipc_isr example
|
||||
|
||||
This example code is in the Public Domain (or CC0 licensed, at your option.)
|
||||
|
||||
Unless required by applicable law or agreed to in writing, this
|
||||
software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
|
||||
CONDITIONS OF ANY KIND, either express or implied.
|
||||
*/
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include "esp_timer.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_ipc_isr.h"
|
||||
#include "sdkconfig.h"
|
||||
@ -36,7 +32,7 @@ void app_main(void)
|
||||
ESP_LOGI(TAG, "Start");
|
||||
uint32_t ps_other_cpu = 0;
|
||||
ESP_LOGI(TAG, "call get_ps_other_cpu");
|
||||
esp_ipc_isr_asm_call_blocking(get_ps_other_cpu, &ps_other_cpu);
|
||||
esp_ipc_isr_call_blocking(get_ps_other_cpu, &ps_other_cpu);
|
||||
ESP_LOGI(TAG, "PS_INTLEVEL = 0x%"PRIx32, ps_other_cpu & XCHAL_PS_INTLEVEL_MASK);
|
||||
ESP_LOGI(TAG, "PS_EXCM = 0x%"PRIx32, (ps_other_cpu & XCHAL_PS_EXCM_MASK) >> XCHAL_PS_EXCM_SHIFT);
|
||||
ESP_LOGI(TAG, "PS_UM = 0x%"PRIx32, (ps_other_cpu & XCHAL_PS_UM_MASK) >> XCHAL_PS_UM_SHIFT);
|
||||
@ -49,10 +45,10 @@ void app_main(void)
|
||||
ESP_LOGI(TAG, "in[0] = 0x%"PRIx32, arg.in[0]);
|
||||
ESP_LOGI(TAG, "in[1] = 0x%"PRIx32, arg.in[1]);
|
||||
ESP_LOGI(TAG, "in[2] = 0x%"PRIx32, arg.in[2]);
|
||||
esp_ipc_isr_asm_call_blocking(extended_ipc_isr_asm, (void*)&arg);
|
||||
esp_ipc_isr_call_blocking(extended_ipc_isr_asm, (void*)&arg);
|
||||
ESP_LOGI(TAG, "out[0] = (in[0] | in[1] | in[2]) = 0x%"PRIx32, arg.out[0]);
|
||||
assert(0x03 == arg.out[0]);
|
||||
ESP_LOGI(TAG, "out[1] = (in[0] & in[1] & in[2]) = 0x%"PRIx32, arg.out[1]);
|
||||
ESP_LOGI(TAG, "out[1] = (in[0] + in[1] + in[2]) = 0x%"PRIx32, arg.out[1]);
|
||||
assert(0x06 == arg.out[1]);
|
||||
ESP_LOGI(TAG, "out[2] = in[2] = 0x%"PRIx32, arg.out[2]);
|
||||
assert(0x03 == arg.out[2]);
|
@ -18,7 +18,7 @@ def test_ipc_isr(dut: Dut) -> None:
|
||||
dut.expect_exact('example: in[1] = 0x2')
|
||||
dut.expect_exact('example: in[2] = 0x3')
|
||||
dut.expect_exact('example: out[0] = (in[0] | in[1] | in[2]) = 0x3')
|
||||
dut.expect_exact('example: out[1] = (in[0] & in[1] & in[2]) = 0x6')
|
||||
dut.expect_exact('example: out[1] = (in[0] + in[1] + in[2]) = 0x6')
|
||||
dut.expect_exact('example: out[2] = in[2] = 0x3')
|
||||
dut.expect_exact('example: out[3] = PS of other cpu = 0x25')
|
||||
dut.expect_exact('example: End')
|
@ -1271,7 +1271,6 @@ examples/system/gcov/main/gcov_example_main.c
|
||||
examples/system/gdbstub/main/gdbstub_main.c
|
||||
examples/system/heap_task_tracking/main/heap_task_tracking_main.c
|
||||
examples/system/himem/main/himem_example_main.c
|
||||
examples/system/ipc/ipc_isr/main/main.c
|
||||
examples/system/ota/advanced_https_ota/main/advanced_https_ota_example.c
|
||||
examples/system/ota/native_ota_example/main/native_ota_example.c
|
||||
examples/system/ota/otatool/main/otatool_main.c
|
||||
|
Loading…
x
Reference in New Issue
Block a user