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

@ -97,7 +97,7 @@ example_test_001B_V3:
example_test_001C: example_test_001C:
extends: .example_test_esp32_template extends: .example_test_esp32_template
parallel: 3 parallel: 4
tags: tags:
- ESP32 - ESP32
- Example_GENERIC - Example_GENERIC

View File

@ -1,6 +1,5 @@
idf_build_get_property(target IDF_TARGET) idf_build_get_property(target IDF_TARGET)
set(priv_requires efuse)
set(requires soc) set(requires soc)
set(priv_requires efuse bootloader_support spi_flash) set(priv_requires efuse bootloader_support spi_flash)
if(${target} STREQUAL "esp32") if(${target} STREQUAL "esp32")
@ -17,7 +16,7 @@ if(NOT BOOTLOADER_BUILD)
"mac_addr.c" "mac_addr.c"
"sleep_modes.c" "sleep_modes.c"
"regi2c_ctrl.c") "regi2c_ctrl.c")
list(APPEND priv_requires esp_ipc) list(APPEND requires esp_ipc)
else() else()
# Requires "_esp_error_check_failed()" function # Requires "_esp_error_check_failed()" function
list(APPEND priv_requires "esp_system") list(APPEND priv_requires "esp_system")

View File

@ -3,30 +3,28 @@
* *
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
#pragma once
#include <stdint.h> #include <stdint.h>
#include <sdkconfig.h> #include <sdkconfig.h>
#ifndef _ESP_DPORT_ACCESS_H_
#define _ESP_DPORT_ACCESS_H_
#include "xtensa/xtruntime.h" #include "xtensa/xtruntime.h"
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
void esp_dport_access_stall_other_cpu_start(void); void esp_dport_access_stall_other_cpu_start(void) __attribute__ ((deprecated));
void esp_dport_access_stall_other_cpu_end(void); void esp_dport_access_stall_other_cpu_end(void) __attribute__ ((deprecated));
void esp_dport_access_int_init(void); void esp_dport_access_int_init(void) __attribute__ ((deprecated));
void esp_dport_access_int_pause(void); void esp_dport_access_int_pause(void) __attribute__ ((deprecated));
void esp_dport_access_int_resume(void); void esp_dport_access_int_resume(void) __attribute__ ((deprecated));
void esp_dport_access_read_buffer(uint32_t *buff_out, uint32_t address, uint32_t num_words); void esp_dport_access_read_buffer(uint32_t *buff_out, uint32_t address, uint32_t num_words);
uint32_t esp_dport_access_reg_read(uint32_t reg); uint32_t esp_dport_access_reg_read(uint32_t reg);
uint32_t esp_dport_access_sequence_reg_read(uint32_t reg); uint32_t esp_dport_access_sequence_reg_read(uint32_t reg);
//This routine does not stop the dport routines in any way that is recoverable. Please //This routine does not stop the dport routines in any way that is recoverable. Please
//only call in case of panic(). //only call in case of panic().
void esp_dport_access_int_abort(void); void esp_dport_access_int_abort(void) __attribute__ ((deprecated));
#if defined(BOOTLOADER_BUILD) || !defined(CONFIG_ESP32_DPORT_WORKAROUND) || !defined(ESP_PLATFORM) #if defined(BOOTLOADER_BUILD) || !defined(CONFIG_ESP32_DPORT_WORKAROUND) || !defined(ESP_PLATFORM)
#define DPORT_STALL_OTHER_CPU_START() #define DPORT_STALL_OTHER_CPU_START()
@ -34,8 +32,9 @@ void esp_dport_access_int_abort(void);
#define DPORT_INTERRUPT_DISABLE() #define DPORT_INTERRUPT_DISABLE()
#define DPORT_INTERRUPT_RESTORE() #define DPORT_INTERRUPT_RESTORE()
#else #else
#define DPORT_STALL_OTHER_CPU_START() esp_dport_access_stall_other_cpu_start() #include "esp_ipc_isr.h"
#define DPORT_STALL_OTHER_CPU_END() esp_dport_access_stall_other_cpu_end() #define DPORT_STALL_OTHER_CPU_START() esp_ipc_isr_stall_other_cpu()
#define DPORT_STALL_OTHER_CPU_END() esp_ipc_isr_release_other_cpu()
#define DPORT_INTERRUPT_DISABLE() unsigned int intLvl = XTOS_SET_INTLEVEL(CONFIG_ESP32_DPORT_DIS_INTERRUPT_LVL) #define DPORT_INTERRUPT_DISABLE() unsigned int intLvl = XTOS_SET_INTLEVEL(CONFIG_ESP32_DPORT_DIS_INTERRUPT_LVL)
#define DPORT_INTERRUPT_RESTORE() XTOS_RESTORE_JUST_INTLEVEL(intLvl) #define DPORT_INTERRUPT_RESTORE() XTOS_RESTORE_JUST_INTLEVEL(intLvl)
#endif #endif
@ -43,5 +42,3 @@ void esp_dport_access_int_abort(void);
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif
#endif /* _ESP_DPORT_ACCESS_H_ */

View File

@ -4,202 +4,11 @@
* SPDX-License-Identifier: Apache-2.0 * SPDX-License-Identifier: Apache-2.0
*/ */
/*
* DPORT access is used for 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 want 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.
*/
#include <stdint.h> #include <stdint.h>
#include <string.h> #include <string.h>
#include <sdkconfig.h>
#include "esp_attr.h"
#include "esp_err.h"
#include "esp_intr_alloc.h"
#include "soc/cpu.h"
#include "soc/dport_reg.h" #include "soc/dport_reg.h"
#include "soc/spi_periph.h" #include "xtensa/core-macros.h"
#include "hal/cpu_hal.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "sdkconfig.h"
#ifndef CONFIG_FREERTOS_UNICORE
static portMUX_TYPE g_dport_mux = portMUX_INITIALIZER_UNLOCKED;
#define DPORT_CORE_STATE_IDLE 0
#define DPORT_CORE_STATE_RUNNING 1
static uint32_t volatile dport_core_state[portNUM_PROCESSORS]; //cpu is already run
/* these global variables are accessed from interrupt vector, hence not declared as static */
uint32_t volatile dport_access_start[portNUM_PROCESSORS]; //dport register could be accessed
uint32_t volatile dport_access_end[portNUM_PROCESSORS]; //dport register is accessed over
static uint32_t volatile dport_access_ref[portNUM_PROCESSORS]; //dport access reference
#ifdef DPORT_ACCESS_BENCHMARK
#define DPORT_ACCESS_BENCHMARK_STORE_NUM
static uint32_t ccount_start[portNUM_PROCESSORS];
static uint32_t ccount_end[portNUM_PROCESSORS];
static uint32_t ccount_margin[portNUM_PROCESSORS][DPORT_ACCESS_BENCHMARK_STORE_NUM];
static uint32_t ccount_margin_cnt;
#endif
static BaseType_t oldInterruptLevel[2];
#endif // CONFIG_FREERTOS_UNICORE
/* stall other cpu that this cpu is pending to access dport register start */
void IRAM_ATTR esp_dport_access_stall_other_cpu_start(void)
{
#ifndef CONFIG_FREERTOS_UNICORE
if (dport_core_state[0] == DPORT_CORE_STATE_IDLE
|| dport_core_state[1] == DPORT_CORE_STATE_IDLE) {
return;
}
BaseType_t intLvl = portENTER_CRITICAL_NESTED();
int cpu_id = xPortGetCoreID();
#ifdef DPORT_ACCESS_BENCHMARK
ccount_start[cpu_id] = cpu_hal_get_cycle_count();
#endif
if (dport_access_ref[cpu_id] == 0) {
portENTER_CRITICAL_ISR(&g_dport_mux);
oldInterruptLevel[cpu_id]=intLvl;
dport_access_start[cpu_id] = 0;
dport_access_end[cpu_id] = 0;
if (cpu_id == 0) {
_DPORT_REG_WRITE(DPORT_CPU_INTR_FROM_CPU_3_REG, DPORT_CPU_INTR_FROM_CPU_3); //interrupt on cpu1
} else {
_DPORT_REG_WRITE(DPORT_CPU_INTR_FROM_CPU_2_REG, DPORT_CPU_INTR_FROM_CPU_2); //interrupt on cpu0
}
while (!dport_access_start[cpu_id]) {};
REG_READ(SPI_DATE_REG(3)); //just read a APB register sure that the APB-bus is idle
}
dport_access_ref[cpu_id]++;
if (dport_access_ref[cpu_id] > 1) {
/* Interrupts are already disabled by the parent, we're nested here. */
portEXIT_CRITICAL_NESTED(intLvl);
}
#endif /* CONFIG_FREERTOS_UNICORE */
}
/* stall other cpu that this cpu is pending to access dport register end */
void IRAM_ATTR esp_dport_access_stall_other_cpu_end(void)
{
#ifndef CONFIG_FREERTOS_UNICORE
int cpu_id = xPortGetCoreID();
if (dport_core_state[0] == DPORT_CORE_STATE_IDLE
|| dport_core_state[1] == DPORT_CORE_STATE_IDLE) {
return;
}
if (dport_access_ref[cpu_id] == 0) {
assert(0);
}
dport_access_ref[cpu_id]--;
if (dport_access_ref[cpu_id] == 0) {
dport_access_end[cpu_id] = 1;
portEXIT_CRITICAL_ISR(&g_dport_mux);
portEXIT_CRITICAL_NESTED(oldInterruptLevel[cpu_id]);
}
#ifdef DPORT_ACCESS_BENCHMARK
ccount_end[cpu_id] = cpu_hal_get_cycle_count();
ccount_margin[cpu_id][ccount_margin_cnt] = ccount_end[cpu_id] - ccount_start[cpu_id];
ccount_margin_cnt = (ccount_margin_cnt + 1)&(DPORT_ACCESS_BENCHMARK_STORE_NUM - 1);
#endif
#endif /* CONFIG_FREERTOS_UNICORE */
}
#ifndef CONFIG_FREERTOS_UNICORE
static void dport_access_init_core(void *arg)
{
int core_id = 0;
uint32_t intr_source = ETS_FROM_CPU_INTR2_SOURCE;
core_id = xPortGetCoreID();
if (core_id == 1) {
intr_source = ETS_FROM_CPU_INTR3_SOURCE;
}
ESP_INTR_DISABLE(ETS_DPORT_INUM);
intr_matrix_set(core_id, intr_source, ETS_DPORT_INUM);
ESP_INTR_ENABLE(ETS_DPORT_INUM);
dport_access_ref[core_id] = 0;
dport_access_start[core_id] = 0;
dport_access_end[core_id] = 0;
dport_core_state[core_id] = DPORT_CORE_STATE_RUNNING;
/* If this fails then the minimum stack size for this config is too close to running out */
assert(uxTaskGetStackHighWaterMark(NULL) > 128);
vTaskDelete(NULL);
}
#endif
/* Defer initialisation until after scheduler is running */
void esp_dport_access_int_init(void)
{
#ifndef CONFIG_FREERTOS_UNICORE
portBASE_TYPE res = xTaskCreatePinnedToCore(&dport_access_init_core, "dport", configMINIMAL_STACK_SIZE, NULL, 5, NULL, xPortGetCoreID());
assert(res == pdTRUE);
(void)res;
#endif
}
void IRAM_ATTR esp_dport_access_int_pause(void)
{
#ifndef CONFIG_FREERTOS_UNICORE
portENTER_CRITICAL_ISR(&g_dport_mux);
dport_core_state[0] = DPORT_CORE_STATE_IDLE;
dport_core_state[1] = DPORT_CORE_STATE_IDLE;
portEXIT_CRITICAL_ISR(&g_dport_mux);
#endif
}
//Used in panic code: the enter_critical stuff may be messed up so we just stop everything without checking the mux.
void IRAM_ATTR esp_dport_access_int_abort(void)
{
#ifndef CONFIG_FREERTOS_UNICORE
dport_core_state[0] = DPORT_CORE_STATE_IDLE;
dport_core_state[1] = DPORT_CORE_STATE_IDLE;
#endif
}
void IRAM_ATTR esp_dport_access_int_resume(void)
{
#ifndef CONFIG_FREERTOS_UNICORE
portENTER_CRITICAL_ISR(&g_dport_mux);
dport_core_state[0] = DPORT_CORE_STATE_RUNNING;
dport_core_state[1] = DPORT_CORE_STATE_RUNNING;
portEXIT_CRITICAL_ISR(&g_dport_mux);
#endif
}
/** /**
* @brief Read a sequence of DPORT registers to the buffer, SMP-safe version. * @brief Read a sequence of DPORT registers to the buffer, SMP-safe version.

View File

@ -24,6 +24,7 @@
#include "esp_log.h" #include "esp_log.h"
#include "esp_newlib.h" #include "esp_newlib.h"
#include "esp_timer.h" #include "esp_timer.h"
#include "esp_ipc_isr.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "soc/soc_caps.h" #include "soc/soc_caps.h"
@ -701,7 +702,7 @@ esp_err_t esp_light_sleep_start(void)
uint64_t frc_time_at_start = esp_system_get_time(); uint64_t frc_time_at_start = esp_system_get_time();
uint32_t sleep_time_overhead_in = (ccount_at_sleep_start - s_config.ccount_ticks_record) / (esp_clk_cpu_freq() / 1000000ULL); uint32_t sleep_time_overhead_in = (ccount_at_sleep_start - s_config.ccount_ticks_record) / (esp_clk_cpu_freq() / 1000000ULL);
DPORT_STALL_OTHER_CPU_START(); esp_ipc_isr_stall_other_cpu();
// Decide which power domains can be powered down // Decide which power domains can be powered down
uint32_t pd_flags = get_power_down_flags(); uint32_t pd_flags = get_power_down_flags();
@ -825,7 +826,7 @@ esp_err_t esp_light_sleep_start(void)
esp_set_time_from_rtc(); esp_set_time_from_rtc();
esp_timer_private_unlock(); esp_timer_private_unlock();
DPORT_STALL_OTHER_CPU_END(); esp_ipc_isr_release_other_cpu();
if (!wdt_was_enabled) { if (!wdt_was_enabled) {
wdt_hal_write_protect_disable(&rtc_wdt_ctx); wdt_hal_write_protect_disable(&rtc_wdt_ctx);
wdt_hal_disable(&rtc_wdt_ctx); wdt_hal_disable(&rtc_wdt_ctx);

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") 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 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 #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
#ifndef CONFIG_FREERTOS_UNICORE
/** @cond */ /** @cond */
typedef void (*esp_ipc_func_t)(void* arg); typedef void (*esp_ipc_func_t)(void* arg);
/** @endcond */ /** @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); 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 #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 <assert.h>
#include "esp_err.h" #include "esp_err.h"
#include "esp_ipc.h" #include "esp_ipc.h"
#include "esp_ipc_isr.h"
#include "esp_attr.h" #include "esp_attr.h"
#include "freertos/FreeRTOS.h" #include "freertos/FreeRTOS.h"
#include "freertos/task.h" #include "freertos/task.h"
#include "freertos/semphr.h" #include "freertos/semphr.h"
#ifndef CONFIG_FREERTOS_UNICORE
static TaskHandle_t s_ipc_task_handle[portNUM_PROCESSORS]; 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_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_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) static void esp_ipc_init(void)
{ {
#ifdef CONFIG_ESP_IPC_ISR_ENABLE
esp_ipc_isr_init();
#endif
char task_name[15]; char task_name[15];
for (int i = 0; i < portNUM_PROCESSORS; ++i) { for (int i = 0; i < portNUM_PROCESSORS; ++i) {
snprintf(task_name, sizeof(task_name), "ipc%d", 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); 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 "." idf_component_register(SRC_DIRS "."
PRIV_INCLUDE_DIRS "." PRIV_INCLUDE_DIRS "."
PRIV_REQUIRES cmock test_utils esp_ipc) 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 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 */

View File

@ -38,7 +38,7 @@ else()
# should be removable once using component init functions # should be removable once using component init functions
# link-time registration is used. # link-time registration is used.
esp_pm app_update nvs_flash pthread app_trace esp_gdbstub esp_pm app_update nvs_flash pthread app_trace esp_gdbstub
espcoredump esp_phy efuse espcoredump esp_phy efuse esp_ipc
LDFRAGMENTS "linker.lf" "app.lf") LDFRAGMENTS "linker.lf" "app.lf")
add_subdirectory(port) add_subdirectory(port)

View File

@ -389,31 +389,6 @@ menu "ESP System Settings"
If this option is enabled, the Task Wtachdog Timer will wach the CPU1 If this option is enabled, the Task Wtachdog Timer will wach the CPU1
Idle Task. Idle Task.
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_PANIC_HANDLER_IRAM config ESP_PANIC_HANDLER_IRAM
bool "Place panic handler code in IRAM" bool "Place panic handler code in IRAM"
default n default n

View File

@ -14,7 +14,7 @@
#include <stdlib.h> #include <stdlib.h>
#include "esp_spi_flash.h" #include "esp_spi_flash.h"
#include "esp_ipc_isr.h"
#include "esp_private/system_internal.h" #include "esp_private/system_internal.h"
#include "soc/soc_memory_layout.h" #include "soc/soc_memory_layout.h"
@ -165,9 +165,7 @@ static void panic_handler(void *frame, bool pseudo_excause)
SOC_HAL_STALL_OTHER_CORES(); SOC_HAL_STALL_OTHER_CORES();
#endif #endif
#if CONFIG_IDF_TARGET_ESP32 esp_ipc_isr_stall_abort();
esp_dport_access_int_abort();
#endif
if (esp_cpu_in_ocd_debug_mode()) { if (esp_cpu_in_ocd_debug_mode()) {
#if __XTENSA__ #if __XTENSA__
@ -205,9 +203,7 @@ static void IRAM_ATTR panic_enable_cache(void) {
int core_id = cpu_hal_get_core_id(); int core_id = cpu_hal_get_core_id();
if (!spi_flash_cache_enabled()) { if (!spi_flash_cache_enabled()) {
#ifdef CONFIG_IDF_TARGET_ESP32 esp_ipc_isr_stall_abort();
esp_dport_access_int_abort();
#endif
spi_flash_enable_cache(core_id); spi_flash_enable_cache(core_id);
} }
} }

View File

@ -1,4 +1,4 @@
set(srcs "dport_panic_highint_hdl.S" set(srcs "highint_hdl.S"
"clk.c" "clk.c"
"reset_reason.c" "reset_reason.c"
"system_internal.c" "system_internal.c"
@ -15,7 +15,7 @@ add_prefix(srcs "${CMAKE_CURRENT_LIST_DIR}/" ${srcs})
target_sources(${COMPONENT_LIB} PRIVATE ${srcs}) target_sources(${COMPONENT_LIB} PRIVATE ${srcs})
#ld_include_panic_highint_hdl is added as an undefined symbol because otherwise the #ld_include_highint_hdl is added as an undefined symbol because otherwise the
#linker will ignore panic_highint_hdl.S as it has no other files depending on any #linker will ignore panic_highint_hdl.S as it has no other files depending on any
#symbols in it. #symbols in it.
set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u ld_include_panic_highint_hdl") set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u ld_include_highint_hdl")

View File

@ -27,7 +27,7 @@
/* /*
Interrupt , a high-priority interrupt, is used for several things: Interrupt , a high-priority interrupt, is used for several things:
- Dport access mediation - IPC_ISR handler
- Cache error panic handler - Cache error panic handler
- Interrupt watchdog panic handler - Interrupt watchdog panic handler
@ -64,11 +64,11 @@ _l4_intr_livelock_pro:
xt_highint4: xt_highint4:
#ifndef CONFIG_FREERTOS_UNICORE #ifndef CONFIG_FREERTOS_UNICORE
/* See if we're here for the dport access interrupt */ /* See if we're here for the IPC_ISR interrupt */
rsr a0, INTERRUPT rsr a0, INTERRUPT
extui a0, a0, ETS_DPORT_INUM, 1 extui a0, a0, ETS_IPC_ISR_INUM, 1
bnez a0, .handle_dport_access_int bnez a0, esp_ipc_isr_handler
#endif // CONFIG_FREERTOS_UNICORE #endif // not CONFIG_FREERTOS_UNICORE
#if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX && CONFIG_ESP_INT_WDT #if CONFIG_ESP32_ECO3_CACHE_LOCK_FIX && CONFIG_ESP_INT_WDT
/* See if we're here for the tg1 watchdog interrupt */ /* See if we're here for the tg1 watchdog interrupt */
@ -382,7 +382,7 @@ xt_highint4:
/* Feed watchdog */ /* Feed watchdog */
8: wdt_feed TIMERG1 8: wdt_feed TIMERG1
9: wsr a0, PS /* restore iterrupt level */ 9: wsr a0, PS /* restore interrupt level */
movi a0, 0 movi a0, 0
beqz a5, 1f beqz a5, 1f
@ -415,75 +415,9 @@ xt_highint4:
#endif #endif
#ifndef CONFIG_FREERTOS_UNICORE
.align 4
.handle_dport_access_int:
/* This section is for dport access register protection */
/* Allocate exception frame and save minimal context. */
/* Because the interrupt cause code has protection that only
allows one cpu to enter in the dport section of the L4
interrupt at one time, there's no need to have two
_l4_intr_stack for each cpu */
/* This int is edge-triggered and needs clearing. */
movi a0, (1<<ETS_DPORT_INUM)
wsr a0, INTCLEAR
/* Save A2, A3, A4 so we can use those registers */
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
/* handle dport interrupt */
/* get CORE_ID */
getcoreid a0
beqz a0, 2f
/* current cpu is 1 */
movi a0, DPORT_CPU_INTR_FROM_CPU_3_REG
movi a2, 0
s32i a2, a0, 0 /* clear intr */
movi a0, 0 /* other cpu id */
j 3f
2:
/* current cpu is 0 */
movi a0, DPORT_CPU_INTR_FROM_CPU_2_REG
movi a2, 0
s32i a2, a0, 0 /* clear intr */
movi a0, 1 /* other cpu id */
3:
rsil a4, CONFIG_ESP32_DPORT_DIS_INTERRUPT_LVL /* disable nested iterrupt */
/* set and wait flag */
movi a2, dport_access_start
addx4 a2, a0, a2
movi a3, 1
s32i a3, a2, 0
memw
movi a2, dport_access_end
addx4 a2, a0, a2
.check_dport_access_end:
l32i a3, a2, 0
beqz a3, .check_dport_access_end
wsr a4, PS /* restore iterrupt level */
/* 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
rsync /* ensure register restored */
rsr a0, EXCSAVE_4 /* restore a0 */
rfi 4
#endif // CONFIG_FREERTOS_UNICORE
/* The linker has no reason to link in this file; all symbols it exports are already defined /* The linker has no reason to link in this file; all symbols it exports are already defined
(weakly!) in the default int handler. Define a symbol here so we can use it to have the (weakly!) in the default int handler. Define a symbol here so we can use it to have the
linker inspect this anyway. */ linker inspect this anyway. */
.global ld_include_panic_highint_hdl .global ld_include_highint_hdl
ld_include_panic_highint_hdl: ld_include_highint_hdl:

View File

@ -18,6 +18,7 @@
#include "esp_attr.h" #include "esp_attr.h"
#include "esp_efuse.h" #include "esp_efuse.h"
#include "esp_log.h" #include "esp_log.h"
#include "esp_ipc_isr.h"
#include "sdkconfig.h" #include "sdkconfig.h"
#include "esp_rom_uart.h" #include "esp_rom_uart.h"
#include "soc/dport_reg.h" #include "soc/dport_reg.h"
@ -67,7 +68,7 @@ void IRAM_ATTR esp_restart_noos(void)
esp_cpu_stall(other_core_id); esp_cpu_stall(other_core_id);
// Other core is now stalled, can access DPORT registers directly // Other core is now stalled, can access DPORT registers directly
esp_dport_access_int_abort(); esp_ipc_isr_stall_abort();
//Todo: Refactor to use Interrupt or Task Watchdog API, and a system level WDT context //Todo: Refactor to use Interrupt or Task Watchdog API, and a system level WDT context
// Disable TG0/TG1 watchdogs // Disable TG0/TG1 watchdogs

View File

@ -1,4 +1,4 @@
set(srcs "dport_panic_highint_hdl.S" set(srcs "highint_hdl.S"
"clk.c" "clk.c"
"reset_reason.c" "reset_reason.c"
"system_internal.c" "system_internal.c"
@ -18,7 +18,7 @@ if(CONFIG_ESP_CONSOLE_USB_CDC)
target_sources(${COMPONENT_LIB} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/usb_console.c") target_sources(${COMPONENT_LIB} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/usb_console.c")
endif() endif()
#ld_include_panic_highint_hdl is added as an undefined symbol because otherwise the #ld_include_highint_hdl is added as an undefined symbol because otherwise the
#linker will ignore panic_highint_hdl.S as it has no other files depending on any #linker will ignore highint_hdl.S as it has no other files depending on any
#symbols in it. #symbols in it.
set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u ld_include_panic_highint_hdl") set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u ld_include_highint_hdl")

View File

@ -106,5 +106,5 @@ xt_highint4:
(weakly!) in the default int handler. Define a symbol here so we can use it to have the (weakly!) in the default int handler. Define a symbol here so we can use it to have the
linker inspect this anyway. */ linker inspect this anyway. */
.global ld_include_panic_highint_hdl .global ld_include_highint_hdl
ld_include_panic_highint_hdl: ld_include_highint_hdl:

View File

@ -1,4 +1,4 @@
set(srcs "dport_panic_highint_hdl.S" set(srcs "highint_hdl.S"
"clk.c" "clk.c"
"reset_reason.c" "reset_reason.c"
"system_internal.c" "system_internal.c"
@ -18,7 +18,7 @@ if(CONFIG_ESP_CONSOLE_USB_CDC)
target_sources(${COMPONENT_LIB} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/usb_console.c") target_sources(${COMPONENT_LIB} PRIVATE "${CMAKE_CURRENT_LIST_DIR}/usb_console.c")
endif() endif()
#ld_include_panic_highint_hdl is added as an undefined symbol because otherwise the #ld_include_highint_hdl is added as an undefined symbol because otherwise the
#linker will ignore panic_highint_hdl.S as it has no other files depending on any #linker will ignore panic_highint_hdl.S as it has no other files depending on any
#symbols in it. #symbols in it.
set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u ld_include_panic_highint_hdl") set_property(TARGET ${COMPONENT_LIB} APPEND PROPERTY INTERFACE_LINK_LIBRARIES "-u ld_include_highint_hdl")

View File

@ -26,6 +26,7 @@
/* /*
Interrupt , a high-priority interrupt, is used for several things: Interrupt , a high-priority interrupt, is used for several things:
- IPC_ISR handler
- Cache error panic handler - Cache error panic handler
- Interrupt watchdog panic handler - Interrupt watchdog panic handler
@ -44,6 +45,13 @@ _l4_intr_stack:
.align 4 .align 4
xt_highint4: xt_highint4:
#ifndef CONFIG_FREERTOS_UNICORE
/* See if we're here for the IPC_ISR interrupt */
rsr a0, INTERRUPT
extui a0, a0, ETS_IPC_ISR_INUM, 1
bnez a0, esp_ipc_isr_handler
#endif // not CONFIG_FREERTOS_UNICORE
/* Allocate exception frame and save minimal context. */ /* Allocate exception frame and save minimal context. */
mov a0, sp mov a0, sp
addi sp, sp, -XT_STK_FRMSZ addi sp, sp, -XT_STK_FRMSZ
@ -126,5 +134,5 @@ xt_highint4:
(weakly!) in the default int handler. Define a symbol here so we can use it to have the (weakly!) in the default int handler. Define a symbol here so we can use it to have the
linker inspect this anyway. */ linker inspect this anyway. */
.global ld_include_panic_highint_hdl .global ld_include_highint_hdl
ld_include_panic_highint_hdl: ld_include_highint_hdl:

View File

@ -77,12 +77,6 @@ void esp_startup_start_app_common(void)
esp_crosscore_int_init(); esp_crosscore_int_init();
#ifndef CONFIG_FREERTOS_UNICORE
#if CONFIG_IDF_TARGET_ESP32
esp_dport_access_int_init();
#endif
#endif
#ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME #ifdef CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME
esp_gdbstub_init(); esp_gdbstub_init();
#endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME #endif // CONFIG_ESP_SYSTEM_GDBSTUB_RUNTIME

View File

@ -518,9 +518,6 @@ void esp_startup_start_app_other_cores(void)
#endif #endif
esp_crosscore_int_init(); esp_crosscore_int_init();
#if CONFIG_IDF_TARGET_ESP32
esp_dport_access_int_init();
#endif
ESP_EARLY_LOGI(TAG, "Starting scheduler on APP CPU."); ESP_EARLY_LOGI(TAG, "Starting scheduler on APP CPU.");
xPortStartScheduler(); xPortStartScheduler();

View File

@ -1156,6 +1156,8 @@
#define DPORT_CPU_INTR_FROM_CPU_1_V 0x1 #define DPORT_CPU_INTR_FROM_CPU_1_V 0x1
#define DPORT_CPU_INTR_FROM_CPU_1_S 0 #define DPORT_CPU_INTR_FROM_CPU_1_S 0
#define SYSTEM_CPU_INTR_FROM_CPU_2_REG DPORT_CPU_INTR_FROM_CPU_2_REG
#define SYSTEM_CPU_INTR_FROM_CPU_2 DPORT_CPU_INTR_FROM_CPU_2
#define DPORT_CPU_INTR_FROM_CPU_2_REG (DR_REG_DPORT_BASE + 0x0E4) #define DPORT_CPU_INTR_FROM_CPU_2_REG (DR_REG_DPORT_BASE + 0x0E4)
/* DPORT_CPU_INTR_FROM_CPU_2 : R/W ;bitpos:[0] ;default: 1'b0 ; */ /* DPORT_CPU_INTR_FROM_CPU_2 : R/W ;bitpos:[0] ;default: 1'b0 ; */
/*description: */ /*description: */
@ -1164,6 +1166,8 @@
#define DPORT_CPU_INTR_FROM_CPU_2_V 0x1 #define DPORT_CPU_INTR_FROM_CPU_2_V 0x1
#define DPORT_CPU_INTR_FROM_CPU_2_S 0 #define DPORT_CPU_INTR_FROM_CPU_2_S 0
#define SYSTEM_CPU_INTR_FROM_CPU_3_REG DPORT_CPU_INTR_FROM_CPU_3_REG
#define SYSTEM_CPU_INTR_FROM_CPU_3 DPORT_CPU_INTR_FROM_CPU_3
#define DPORT_CPU_INTR_FROM_CPU_3_REG (DR_REG_DPORT_BASE + 0x0E8) #define DPORT_CPU_INTR_FROM_CPU_3_REG (DR_REG_DPORT_BASE + 0x0E8)
/* DPORT_CPU_INTR_FROM_CPU_3 : R/W ;bitpos:[0] ;default: 1'b0 ; */ /* DPORT_CPU_INTR_FROM_CPU_3 : R/W ;bitpos:[0] ;default: 1'b0 ; */
/*description: */ /*description: */

View File

@ -307,8 +307,8 @@
#define ETS_GPIO_NMI_SOURCE 23/**< interrupt of GPIO, NMI*/ #define ETS_GPIO_NMI_SOURCE 23/**< interrupt of GPIO, NMI*/
#define ETS_FROM_CPU_INTR0_SOURCE 24/**< interrupt0 generated from a CPU, level*/ /* Used for FreeRTOS */ #define ETS_FROM_CPU_INTR0_SOURCE 24/**< interrupt0 generated from a CPU, level*/ /* Used for FreeRTOS */
#define ETS_FROM_CPU_INTR1_SOURCE 25/**< interrupt1 generated from a CPU, level*/ /* Used for FreeRTOS */ #define ETS_FROM_CPU_INTR1_SOURCE 25/**< interrupt1 generated from a CPU, level*/ /* Used for FreeRTOS */
#define ETS_FROM_CPU_INTR2_SOURCE 26/**< interrupt2 generated from a CPU, level*/ /* Used for DPORT Access */ #define ETS_FROM_CPU_INTR2_SOURCE 26/**< interrupt2 generated from a CPU, level*/ /* Used for IPC_ISR */
#define ETS_FROM_CPU_INTR3_SOURCE 27/**< interrupt3 generated from a CPU, level*/ /* Used for DPORT Access */ #define ETS_FROM_CPU_INTR3_SOURCE 27/**< interrupt3 generated from a CPU, level*/ /* Used for IPC_ISR */
#define ETS_SPI0_INTR_SOURCE 28/**< interrupt of SPI0, level, SPI0 is for Cache Access, do not use this*/ #define ETS_SPI0_INTR_SOURCE 28/**< interrupt of SPI0, level, SPI0 is for Cache Access, do not use this*/
#define ETS_SPI1_INTR_SOURCE 29/**< interrupt of SPI1, level, SPI1 is for flash read/write, do not use this*/ #define ETS_SPI1_INTR_SOURCE 29/**< interrupt of SPI1, level, SPI1 is for flash read/write, do not use this*/
#define ETS_SPI2_INTR_SOURCE 30/**< interrupt of SPI2, level*/ #define ETS_SPI2_INTR_SOURCE 30/**< interrupt of SPI2, level*/
@ -382,7 +382,7 @@
* 25 4 extern level CACHEERR * 25 4 extern level CACHEERR
* 26 5 extern level * 26 5 extern level
* 27 3 extern level Reserved Reserved * 27 3 extern level Reserved Reserved
* 28 4 extern edge DPORT ACCESS DPORT ACCESS * 28 4 extern edge IPC_ISR IPC_ISR
* 29 3 software Reserved Reserved * 29 3 software Reserved Reserved
* 30 4 extern edge Reserved Reserved * 30 4 extern edge Reserved Reserved
* 31 5 extern level * 31 5 extern level
@ -399,7 +399,7 @@
#define ETS_MEMACCESS_ERR_INUM 25 #define ETS_MEMACCESS_ERR_INUM 25
/* backwards compatibility only, use ETS_MEMACCESS_ERR_INUM instead*/ /* backwards compatibility only, use ETS_MEMACCESS_ERR_INUM instead*/
#define ETS_CACHEERR_INUM ETS_MEMACCESS_ERR_INUM #define ETS_CACHEERR_INUM ETS_MEMACCESS_ERR_INUM
#define ETS_DPORT_INUM 28 #define ETS_IPC_ISR_INUM 28
//CPU0 Interrupt number used in ROM, should be cancelled in SDK //CPU0 Interrupt number used in ROM, should be cancelled in SDK
#define ETS_SLC_INUM 1 #define ETS_SLC_INUM 1

View File

@ -102,8 +102,8 @@ typedef enum {
ETS_SHA_INTR_SOURCE, /**< interrupt of SHA accelerator, level*/ ETS_SHA_INTR_SOURCE, /**< interrupt of SHA accelerator, level*/
ETS_FROM_CPU_INTR0_SOURCE, /**< interrupt0 generated from a CPU, level*/ /* Used for FreeRTOS */ ETS_FROM_CPU_INTR0_SOURCE, /**< interrupt0 generated from a CPU, level*/ /* Used for FreeRTOS */
ETS_FROM_CPU_INTR1_SOURCE, /**< interrupt1 generated from a CPU, level*/ /* Used for FreeRTOS */ ETS_FROM_CPU_INTR1_SOURCE, /**< interrupt1 generated from a CPU, level*/ /* Used for FreeRTOS */
ETS_FROM_CPU_INTR2_SOURCE, /**< interrupt2 generated from a CPU, level*/ /* Used for DPORT Access */ ETS_FROM_CPU_INTR2_SOURCE, /**< interrupt2 generated from a CPU, level*/
ETS_FROM_CPU_INTR3_SOURCE, /**< interrupt3 generated from a CPU, level*/ /* Used for DPORT Access */ ETS_FROM_CPU_INTR3_SOURCE, /**< interrupt3 generated from a CPU, level*/
ETS_ASSIST_DEBUG_INTR_SOURCE, /**< interrupt of Assist debug module, LEVEL*/ ETS_ASSIST_DEBUG_INTR_SOURCE, /**< interrupt of Assist debug module, LEVEL*/
ETS_DMA_APBPERI_PMS_INTR_SOURCE, ETS_DMA_APBPERI_PMS_INTR_SOURCE,
ETS_CORE0_IRAM0_PMS_INTR_SOURCE, ETS_CORE0_IRAM0_PMS_INTR_SOURCE,

View File

@ -317,7 +317,6 @@
#define ETS_T1_WDT_INUM 24 #define ETS_T1_WDT_INUM 24
#define ETS_CACHEERR_INUM 25 #define ETS_CACHEERR_INUM 25
#define ETS_MEMPROT_ERR_INUM 26 #define ETS_MEMPROT_ERR_INUM 26
#define ETS_DPORT_INUM 28
//CPU0 Max valid interrupt number //CPU0 Max valid interrupt number
#define ETS_MAX_INUM 31 #define ETS_MAX_INUM 31

View File

@ -102,8 +102,8 @@ typedef enum {
ETS_SHA_INTR_SOURCE, /**< interrupt of SHA accelerator, level*/ ETS_SHA_INTR_SOURCE, /**< interrupt of SHA accelerator, level*/
ETS_FROM_CPU_INTR0_SOURCE, /**< interrupt0 generated from a CPU, level*/ /* Used for FreeRTOS */ ETS_FROM_CPU_INTR0_SOURCE, /**< interrupt0 generated from a CPU, level*/ /* Used for FreeRTOS */
ETS_FROM_CPU_INTR1_SOURCE, /**< interrupt1 generated from a CPU, level*/ /* Used for FreeRTOS */ ETS_FROM_CPU_INTR1_SOURCE, /**< interrupt1 generated from a CPU, level*/ /* Used for FreeRTOS */
ETS_FROM_CPU_INTR2_SOURCE, /**< interrupt2 generated from a CPU, level*/ /* Used for DPORT Access */ ETS_FROM_CPU_INTR2_SOURCE, /**< interrupt2 generated from a CPU, level*/
ETS_FROM_CPU_INTR3_SOURCE, /**< interrupt3 generated from a CPU, level*/ /* Used for DPORT Access */ ETS_FROM_CPU_INTR3_SOURCE, /**< interrupt3 generated from a CPU, level*/
ETS_ASSIST_DEBUG_INTR_SOURCE, /**< interrupt of Assist debug module, LEVEL*/ ETS_ASSIST_DEBUG_INTR_SOURCE, /**< interrupt of Assist debug module, LEVEL*/
ETS_DMA_APBPERI_PMS_INTR_SOURCE, ETS_DMA_APBPERI_PMS_INTR_SOURCE,
ETS_CORE0_IRAM0_PMS_INTR_SOURCE, ETS_CORE0_IRAM0_PMS_INTR_SOURCE,

View File

@ -347,7 +347,6 @@
#define ETS_T1_WDT_INUM 24 #define ETS_T1_WDT_INUM 24
#define ETS_CACHEERR_INUM 25 #define ETS_CACHEERR_INUM 25
#define ETS_MEMPROT_ERR_INUM 26 #define ETS_MEMPROT_ERR_INUM 26
#define ETS_DPORT_INUM 28
//CPU0 Max valid interrupt number //CPU0 Max valid interrupt number
#define ETS_MAX_INUM 31 #define ETS_MAX_INUM 31

View File

@ -86,8 +86,8 @@ typedef enum {
ETS_DEDICATED_GPIO_INTR_SOURCE, /**< interrupt of dedicated GPIO, level*/ ETS_DEDICATED_GPIO_INTR_SOURCE, /**< interrupt of dedicated GPIO, level*/
ETS_FROM_CPU_INTR0_SOURCE, /**< interrupt0 generated from a CPU, level*/ /* Used for FreeRTOS */ ETS_FROM_CPU_INTR0_SOURCE, /**< interrupt0 generated from a CPU, level*/ /* Used for FreeRTOS */
ETS_FROM_CPU_INTR1_SOURCE, /**< interrupt1 generated from a CPU, level*/ /* Used for FreeRTOS */ ETS_FROM_CPU_INTR1_SOURCE, /**< interrupt1 generated from a CPU, level*/ /* Used for FreeRTOS */
ETS_FROM_CPU_INTR2_SOURCE, /**< interrupt2 generated from a CPU, level*/ /* Used for DPORT Access */ ETS_FROM_CPU_INTR2_SOURCE, /**< interrupt2 generated from a CPU, level*/
ETS_FROM_CPU_INTR3_SOURCE, /**< interrupt3 generated from a CPU, level*/ /* Used for DPORT Access */ ETS_FROM_CPU_INTR3_SOURCE, /**< interrupt3 generated from a CPU, level*/
ETS_SPI1_INTR_SOURCE = 32, /**< interrupt of SPI1, level, SPI1 is for flash read/write, do not use this*/ ETS_SPI1_INTR_SOURCE = 32, /**< interrupt of SPI1, level, SPI1 is for flash read/write, do not use this*/
ETS_SPI2_INTR_SOURCE, /**< interrupt of SPI2, level*/ ETS_SPI2_INTR_SOURCE, /**< interrupt of SPI2, level*/

View File

@ -299,23 +299,23 @@
//interrupt cpu using table, Please see the core-isa.h //interrupt cpu using table, Please see the core-isa.h
/************************************************************************************************************* /*************************************************************************************************************
* Intr num Level Type PRO CPU usage APP CPU uasge * Intr num Level Type PRO CPU usage
* 0 1 extern level WMAC Reserved * 0 1 extern level WMAC
* 1 1 extern level BT/BLE Host HCI DMA BT/BLE Host HCI DMA * 1 1 extern level BT/BLE Host HCI DMA
* 2 1 extern level * 2 1 extern level
* 3 1 extern level * 3 1 extern level
* 4 1 extern level WBB * 4 1 extern level WBB
* 5 1 extern level BT/BLE Controller BT/BLE Controller * 5 1 extern level BT/BLE Controller
* 6 1 timer FreeRTOS Tick(L1) FreeRTOS Tick(L1) * 6 1 timer FreeRTOS Tick(L1)
* 7 1 software BT/BLE VHCI BT/BLE VHCI * 7 1 software BT/BLE VHCI
* 8 1 extern level BT/BLE BB(RX/TX) BT/BLE BB(RX/TX) * 8 1 extern level BT/BLE BB(RX/TX)
* 9 1 extern level * 9 1 extern level
* 10 1 extern edge * 10 1 extern edge
* 11 3 profiling * 11 3 profiling
* 12 1 extern level * 12 1 extern level
* 13 1 extern level * 13 1 extern level
* 14 7 nmi Reserved Reserved * 14 7 nmi Reserved
* 15 3 timer FreeRTOS Tick(L3) FreeRTOS Tick(L3) * 15 3 timer FreeRTOS Tick(L3)
* 16 5 timer * 16 5 timer
* 17 1 extern level * 17 1 extern level
* 18 1 extern level * 18 1 extern level
@ -327,10 +327,10 @@
* 24 4 extern level TG1_WDT * 24 4 extern level TG1_WDT
* 25 4 extern level CACHEERR * 25 4 extern level CACHEERR
* 26 5 extern level * 26 5 extern level
* 27 3 extern level Reserved Reserved * 27 3 extern level Reserved
* 28 4 extern edge DPORT ACCESS DPORT ACCESS * 28 4 extern edge Reserved
* 29 3 software Reserved Reserved * 29 3 software Reserved
* 30 4 extern edge Reserved Reserved * 30 4 extern edge Reserved
* 31 5 extern level * 31 5 extern level
************************************************************************************************************* *************************************************************************************************************
*/ */
@ -343,7 +343,6 @@
#define ETS_FRC1_INUM 22 #define ETS_FRC1_INUM 22
#define ETS_T1_WDT_INUM 24 #define ETS_T1_WDT_INUM 24
#define ETS_MEMACCESS_ERR_INUM 25 #define ETS_MEMACCESS_ERR_INUM 25
#define ETS_DPORT_INUM 28
//CPU0 Interrupt number used in ROM, should be cancelled in SDK //CPU0 Interrupt number used in ROM, should be cancelled in SDK
#define ETS_SLC_INUM 1 #define ETS_SLC_INUM 1

View File

@ -139,8 +139,8 @@ typedef enum {
ETS_SHA_INTR_SOURCE, /**< interrupt of SHA accelerator, level*/ ETS_SHA_INTR_SOURCE, /**< interrupt of SHA accelerator, level*/
ETS_FROM_CPU_INTR0_SOURCE, /**< interrupt0 generated from a CPU, level*/ /* Used for FreeRTOS */ ETS_FROM_CPU_INTR0_SOURCE, /**< interrupt0 generated from a CPU, level*/ /* Used for FreeRTOS */
ETS_FROM_CPU_INTR1_SOURCE, /**< interrupt1 generated from a CPU, level*/ /* Used for FreeRTOS */ ETS_FROM_CPU_INTR1_SOURCE, /**< interrupt1 generated from a CPU, level*/ /* Used for FreeRTOS */
ETS_FROM_CPU_INTR2_SOURCE, /**< interrupt2 generated from a CPU, level*/ /* Used for DPORT Access */ ETS_FROM_CPU_INTR2_SOURCE, /**< interrupt2 generated from a CPU, level*/ /* Used for IPC_ISR */
ETS_FROM_CPU_INTR3_SOURCE, /**< interrupt3 generated from a CPU, level*/ /* Used for DPORT Access */ ETS_FROM_CPU_INTR3_SOURCE, /**< interrupt3 generated from a CPU, level*/ /* Used for IPC_ISR */
ETS_ASSIST_DEBUG_INTR_SOURCE, /**< interrupt of Assist debug module, LEVEL*/ ETS_ASSIST_DEBUG_INTR_SOURCE, /**< interrupt of Assist debug module, LEVEL*/
ETS_DMA_APBPERI_PMS_INTR_SOURCE, ETS_DMA_APBPERI_PMS_INTR_SOURCE,
ETS_CORE0_IRAM0_PMS_INTR_SOURCE, ETS_CORE0_IRAM0_PMS_INTR_SOURCE,

View File

@ -331,7 +331,7 @@
* 25 4 extern level CACHEERR * 25 4 extern level CACHEERR
* 26 5 extern level * 26 5 extern level
* 27 3 extern level Reserved Reserved * 27 3 extern level Reserved Reserved
* 28 4 extern edge DPORT ACCESS DPORT ACCESS * 28 4 extern edge IPC_ISR IPC_ISR
* 29 3 software Reserved Reserved * 29 3 software Reserved Reserved
* 30 4 extern edge Reserved Reserved * 30 4 extern edge Reserved Reserved
* 31 5 extern level * 31 5 extern level
@ -346,7 +346,7 @@
#define ETS_FRC1_INUM 22 #define ETS_FRC1_INUM 22
#define ETS_T1_WDT_INUM 24 #define ETS_T1_WDT_INUM 24
#define ETS_CACHEERR_INUM 25 #define ETS_CACHEERR_INUM 25
#define ETS_DPORT_INUM 28 #define ETS_IPC_ISR_INUM 28
//CPU0 Interrupt number used in ROM, should be cancelled in SDK //CPU0 Interrupt number used in ROM, should be cancelled in SDK
#define ETS_SLC_INUM 1 #define ETS_SLC_INUM 1

View File

@ -31,7 +31,7 @@ Using these symbols is done by creating an assembly file (suffix .S) and definin
rsr a0, EXCSAVE_5 rsr a0, EXCSAVE_5
rfi 5 rfi 5
For a real-life example, see the :component_file:`esp_system/port/soc/{IDF_TARGET_PATH_NAME}/dport_panic_highint_hdl.S` file; the panic handler interrupt is implemented there. For a real-life example, see the :component_file:`esp_system/port/soc/{IDF_TARGET_PATH_NAME}/highint_hdl.S` file; the panic handler interrupt is implemented there.
Notes Notes

View File

@ -35,9 +35,53 @@ mutex hence simultaneous IPC calls are not possible.
Care should taken to avoid deadlock when writing functions to be executed by Care should taken to avoid deadlock when writing functions to be executed by
IPC, especially when attempting to take a mutex within the function. IPC, especially when attempting to take a mutex within the function.
.. _hi_priority_ipc:
Hi-priority IPC
---------------
In some cases, we need to quickly get the state of the other CPU (for example, in the core dump, in the GDB stub, in some unit tests, in the dport workaround, etc.), and usually it can be implemented as a fairly simple function and can be written in assembler.
For these purposes, the IPC API has special functions :cpp:func:`esp_ipc_isr_asm_call` and :cpp:func:`esp_ipc_isr_asm_call_blocking`.
The `esp_ipc_isr_asm...(asm_func, arg)` functions trigger the High-priority interrupt (4 level) on the other CPU. The given assembler function (asm_func) will run on the other CPU in the context of the interrupt. This assembler function will be called by the CALLX0 command therefore it must be written in assembler.
:cpp:func:`esp_ipc_isr_asm_call` - triggers the Hi-priority interrupt on a particular core to execute a given function. The CPU which called this function will be blocked until the other CPU begins execution of the given function.
:cpp:func:`esp_ipc_isr_asm_call_blocking` is similar but will block the calling CPU until the other CPU has completed the execution of the given function.
:cpp:func:`esp_ipc_isr_stall_other_cpu` stalls the other CPU and the calling CPU disables interrupts with level 3 and lower. To finish stalling the other CPU call :cpp:func:`esp_ipc_isr_release_other_cpu`. The stalled CPU disables interrupts with level 5 and lower.
Functions executed by Hi-priority IPC must be functions of type `void func(void *arg)`. Examples of a assembler function see in :idf_file:`components/esp_ipc/src/esp_ipc_isr/esp_ipc_isr_routines.S`, :idf_file:`components/esp_ipc/test/test_ipc_isr.S` and below. In the asm function, you can use only a few registers as they were saved in the interrupt handler before calling this function, their use is safe. The registers:`a2` as `void *arg`, a3 and a4 are free for use.
Some feature:
- The asm function should be placed in the IRAM memory and aligned on a memory address multiple of 4.
- As the asm function is run in the context of the High-priority interrupt, a C function can no be called because the windows spill is disabled.
- Use only a2, a3 and a4 registers in the asm function (to workaround it see esp_complex_asm_func).
- A CPU, that called these APIs `esp_ipc_isr_asm...(asm_func, arg)`, disables interrupts with level 3 and lower.
- A CPU, where the asm function is executed, disables interrupts with level 5 and lower.
- You do not need to take care about handling of the Hi-priority interrupt.
.. code-block:: asm
/* 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
This number of registers available for use is sufficient for most simple cases. But if you need to run a more complex asm function, you can pass as an argument a pointer to a structure that can accept additional registers to a buffer to make them free to use. Remember to restore them before returning See the :example:`system/ipc/ipc_isr`.
API Reference API Reference
------------- -------------
.. include-build-file:: inc/esp_ipc.inc .. include-build-file:: inc/esp_ipc.inc

View 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.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(ipc_isr)

View File

@ -0,0 +1,8 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := ipc_isr
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,56 @@
| Supported Targets | ESP32 | ESP32-S3 |
| ----------------- | ----- | -------- |
# Hi-priority IPC example
This example demonstrates how to use Hi-priority IPC, it is based on Hi-priority interrupt (level 4). This feature can be useful in case when need quickly to get the state of the other CPU (consult the [Hi-priority IPC](https://docs.espressif.com/projects/esp-idf/en/latest/esp32/api-reference/system/ipc.html#hi-priority-ipc)).
In the `asm_funcs.S` file are functions that will be run on the other core. The function should be fairly simple that can be written in assembler.
The first asm function `get_ps_other_cpu()` demonstrates a simple way of usage, it returns the PS register of other core.
The second asm function `extended_ipc_isr_asm()` demonstrates way when need to do some complex operations on other core, for this, save registers to the buffer (regs[]) making them available for use. Then using passed arguments (in[]) do some work and write the result to out[]. At the end recover saved registers.
## How to use example
### Hardware Required
Example should be able to run on any commonly available ESP32 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 (304) example: Start
I (304) example: call get_ps_other_cpu
I (314) example: PS_INTLEVEL = 0x5
I (314) example: PS_EXCM = 0x0
I (324) example: PS_UM = 0x1
I (324) example: call extended_ipc_isr_asm
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 (354) example: out[2] = in[2] = 0x3
I (354) example: out[3] = PS of other cpu = 0x25
I (364) example: End
```

View File

@ -0,0 +1,28 @@
from __future__ import unicode_literals
import ttfw_idf
@ttfw_idf.idf_example_test(env_tag='Example_GENERIC', target=['esp32'])
def test_examples_ipc_isr(env, _): # type: (ttfw_idf.TinyFW.Env, None) -> None
dut = env.get_dut('ipc_isr', 'examples/system/ipc/ipc_isr')
dut.start_app()
dut.expect_all('example: Start',
'example: PS_INTLEVEL = 0x5',
'example: PS_EXCM = 0x0',
'example: PS_UM = 0x1',
'example: in[0] = 0x1',
'example: in[1] = 0x2',
'example: in[2] = 0x3',
'example: out[0] = (in[0] | in[1] | in[2]) = 0x3',
'example: out[1] = (in[0] & in[1] & in[2]) = 0x6',
'example: out[2] = in[2] = 0x3',
'example: out[3] = PS of other cpu = 0x25',
'example: End',
timeout=10)
if __name__ == '__main__':
test_examples_ipc_isr()

View File

@ -0,0 +1,3 @@
idf_component_register(SRCS "main.c"
"asm_funcs.S"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,89 @@
/*
* 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>
/* get_ps_other_cpu(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.
* Returns PS.
*/
.section .iram1, "ax"
.align 4
.global get_ps_other_cpu
.type get_ps_other_cpu, @function
// Args:
// a2 - void* arg
get_ps_other_cpu:
rsr a3, PS
s32i a3, a2, 0
ret
/* extended_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.
* This function receives a structure (arg) where can be saved some regs
* to get them available here, at the end of the function we recover the saved regs.
*/
.section .iram1, "ax"
.align 4
.global extended_ipc_isr_asm
.type extended_ipc_isr_asm, @function
// Args:
// a2 - arg_data_t* arg
extended_ipc_isr_asm:
/* save all registers (a5-a15 -> regs[11]) */
s32i a5, a2, 0
s32i a6, a2, 4
s32i a7, a2, 8
s32i a8, a2, 12
s32i a9, a2, 16
s32i a10, a2, 20
s32i a11, a2, 24
s32i a12, a2, 28
s32i a13, a2, 32
s32i a14, a2, 36
s32i a15, a2, 40
/* do some work with a2 - a15 */
l32i a5, a2, 44 /* a5 <- in[0] */
l32i a6, a2, 48 /* a6 <- in[1] */
l32i a7, a2, 52 /* a7 <- in[2] */
or a8, a5, a6
or a8, a8, a7
add a9, a5, a6
add a9, a9, a7
mov a10, a7
rsr a11, PS
s32i a8, a2, 56 /* a8 -> out[0] */
s32i a9, a2, 60 /* a9 -> out[1] */
s32i a10, a2, 64 /* a10 -> out[2] */
s32i a11, a2, 68 /* a11 -> out[3] */
/* restore all saved registers (regs[11] -> a5-a15) */
l32i a5, a2, 0
l32i a6, a2, 4
l32i a7, a2, 8
l32i a8, a2, 12
l32i a9, a2, 16
l32i a10, a2, 20
l32i a11, a2, 24
l32i a12, a2, 28
l32i a13, a2, 32
l32i a14, a2, 36
l32i a15, a2, 40
ret

View File

@ -0,0 +1,4 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@ -0,0 +1,61 @@
/* 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.
*/
#include <stdio.h>
#include <string.h>
#include "esp_timer.h"
#include "esp_log.h"
#include "esp_ipc_isr.h"
#include "sdkconfig.h"
#if __XTENSA__
#include "xtensa/config/core.h"
#else
#error "Doesn't support other architectures"
#endif
static const char* TAG = "example";
typedef struct {
uint32_t regs[11];
uint32_t in[3];
uint32_t out[4];
} arg_data_t;
void get_ps_other_cpu(void* arg);
void extended_ipc_isr_asm(void* arg);
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_LOGI(TAG, "PS_INTLEVEL = 0x%x", ps_other_cpu & XCHAL_PS_INTLEVEL_MASK);
ESP_LOGI(TAG, "PS_EXCM = 0x%x", (ps_other_cpu & XCHAL_PS_EXCM_MASK) >> XCHAL_PS_EXCM_SHIFT);
ESP_LOGI(TAG, "PS_UM = 0x%x", (ps_other_cpu & XCHAL_PS_UM_MASK) >> XCHAL_PS_UM_SHIFT);
ESP_LOGI(TAG, "call extended_ipc_isr_asm");
arg_data_t arg = { 0 };
arg.in[0] = 0x01;
arg.in[1] = 0x02;
arg.in[2] = 0x03;
ESP_LOGI(TAG, "in[0] = 0x%x", arg.in[0]);
ESP_LOGI(TAG, "in[1] = 0x%x", arg.in[1]);
ESP_LOGI(TAG, "in[2] = 0x%x", arg.in[2]);
esp_ipc_isr_asm_call_blocking(extended_ipc_isr_asm, (void*)&arg);
ESP_LOGI(TAG, "out[0] = (in[0] | in[1] | in[2]) = 0x%x", arg.out[0]);
assert(0x03 == arg.out[0]);
ESP_LOGI(TAG, "out[1] = (in[0] & in[1] & in[2]) = 0x%x", arg.out[1]);
assert(0x06 == arg.out[1]);
ESP_LOGI(TAG, "out[2] = in[2] = 0x%x", arg.out[2]);
assert(0x03 == arg.out[2]);
ESP_LOGI(TAG, "out[3] = PS of other cpu = 0x%x", arg.out[3]);
assert(ps_other_cpu == arg.out[3]);
ESP_LOGI(TAG, "End");
}