mirror of
https://github.com/espressif/esp-idf
synced 2025-03-09 17:19:09 -04:00
Merge branch 'feat/intr_alloc_force_intrno' into 'master'
feat(esp_hw_support): add the possibility to allocate two sources to the same interrupt line Closes IDF-9552 See merge request espressif/esp-idf!35473
This commit is contained in:
commit
8dfcafa1b2
@ -94,7 +94,7 @@ extern "C" {
|
||||
* the int can be left enabled while the flash cache is disabled.
|
||||
*
|
||||
* @return ESP_ERR_INVALID_ARG if cpu or intno is invalid
|
||||
* ESP_OK otherwise
|
||||
* ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_intr_mark_shared(int intno, int cpu, bool is_in_iram);
|
||||
|
||||
@ -108,7 +108,7 @@ esp_err_t esp_intr_mark_shared(int intno, int cpu, bool is_in_iram);
|
||||
* @param cpu CPU on which the interrupt should be marked as shared (0 or 1)
|
||||
*
|
||||
* @return ESP_ERR_INVALID_ARG if cpu or intno is invalid
|
||||
* ESP_OK otherwise
|
||||
* ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_intr_reserve(int intno, int cpu);
|
||||
|
||||
@ -131,9 +131,10 @@ esp_err_t esp_intr_reserve(int intno, int cpu);
|
||||
* @param flags An ORred mask of the ESP_INTR_FLAG_* defines. These restrict the
|
||||
* choice of interrupts that this routine can choose from. If this value
|
||||
* is 0, it will default to allocating a non-shared interrupt of level
|
||||
* 1, 2 or 3. If this is ESP_INTR_FLAG_SHARED, it will allocate a shared
|
||||
* interrupt of level 1. Setting ESP_INTR_FLAG_INTRDISABLED will return
|
||||
* from this function with the interrupt disabled.
|
||||
* 1, 2 or 3. If ESP_INTR_FLAG_SHARED mask is provided, a shared interrupt of
|
||||
* the given level will be allocated (or level 1 if not specified).
|
||||
* Setting ESP_INTR_FLAG_INTRDISABLED will return from this function with the
|
||||
* interrupt disabled.
|
||||
* @param handler The interrupt handler. Must be NULL when an interrupt of level >3
|
||||
* is requested, because these types of interrupts aren't C-callable.
|
||||
* @param arg Optional argument for passed to the interrupt handler
|
||||
@ -143,13 +144,13 @@ esp_err_t esp_intr_reserve(int intno, int cpu);
|
||||
*
|
||||
* @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid.
|
||||
* ESP_ERR_NOT_FOUND No free interrupt found with the specified flags
|
||||
* ESP_OK otherwise
|
||||
* ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *arg, intr_handle_t *ret_handle);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Allocate an interrupt with the given parameters.
|
||||
* @brief Allocate an interrupt with the given parameters, including an interrupt status register.
|
||||
*
|
||||
*
|
||||
* This essentially does the same as esp_intr_alloc, but allows specifying a register and mask
|
||||
@ -165,9 +166,10 @@ esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *ar
|
||||
* @param flags An ORred mask of the ESP_INTR_FLAG_* defines. These restrict the
|
||||
* choice of interrupts that this routine can choose from. If this value
|
||||
* is 0, it will default to allocating a non-shared interrupt of level
|
||||
* 1, 2 or 3. If this is ESP_INTR_FLAG_SHARED, it will allocate a shared
|
||||
* interrupt of level 1. Setting ESP_INTR_FLAG_INTRDISABLED will return
|
||||
* from this function with the interrupt disabled.
|
||||
* 1, 2 or 3. If ESP_INTR_FLAG_SHARED mask is provided, a shared interrupt of
|
||||
* the given level will be allocated (or level 1 if not specified).
|
||||
* Setting ESP_INTR_FLAG_INTRDISABLED will return from this function with the
|
||||
* interrupt disabled.
|
||||
* @param intrstatusreg The address of an interrupt status register
|
||||
* @param intrstatusmask A mask. If a read of address intrstatusreg has any of the bits
|
||||
* that are 1 in the mask set, the ISR will be called. If not, it will be
|
||||
@ -181,11 +183,92 @@ esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *ar
|
||||
*
|
||||
* @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid.
|
||||
* ESP_ERR_NOT_FOUND No free interrupt found with the specified flags
|
||||
* ESP_OK otherwise
|
||||
* ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusreg, uint32_t intrstatusmask, intr_handler_t handler, void *arg, intr_handle_t *ret_handle);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Allocate an interrupt with the given parameters that can be bound to an existing interrupt handler.
|
||||
*
|
||||
*
|
||||
* This function does the same as esp_intr_alloc, but allows specifying a previously allocated handler as
|
||||
* the interrupt to share with the given source. This can be very handy to treat two pre-determined interrupt
|
||||
* sources in the same interrupt handler. The interrupt will be allocated on the same core as the given
|
||||
* `shared_handle`. Moreover, make sure to specify the same interrupt level as the one being used by `shared_handle`
|
||||
* to prevent any failure from this function.
|
||||
*
|
||||
* @param source The interrupt source. One of the ETS_*_INTR_SOURCE interrupt mux
|
||||
* sources, as defined in soc/soc.h, or one of the internal
|
||||
* ETS_INTERNAL_*_INTR_SOURCE sources as defined in this header.
|
||||
* @param flags An ORred mask of the ESP_INTR_FLAG_* defines. These restrict the
|
||||
* choice of interrupts that this routine can choose from. If this value
|
||||
* is 0, it will default to allocating a non-shared interrupt of level
|
||||
* 1, 2 or 3. If ESP_INTR_FLAG_SHARED mask is provided, a shared interrupt of
|
||||
* the given level will be allocated (or level 1 if not specified).
|
||||
* Setting ESP_INTR_FLAG_INTRDISABLED will return from this function with the
|
||||
* interrupt disabled.
|
||||
* @param handler The interrupt handler. Must be NULL when an interrupt of level >3
|
||||
* is requested, because these types of interrupts aren't C-callable.
|
||||
* @param arg Optional argument for passed to the interrupt handler
|
||||
* @param shared_handle Previously allocated interrupt to share the CPU interrupt line with. If NULL,
|
||||
* calling this function equivalent to esp_intr_alloc, else, ESP_INTR_FLAG_SHARED must
|
||||
* be provided in the flags parameter.
|
||||
* @param ret_handle Pointer to an intr_handle_t to store a handle that can later be
|
||||
* used to request details or free the interrupt. Can be NULL if no handle
|
||||
* is required.
|
||||
*
|
||||
* @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid.
|
||||
* ESP_ERR_NOT_FOUND No free interrupt found with the specified flasg or the given level is different
|
||||
* from the one assigned to the share_handle parameter.
|
||||
* ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_intr_alloc_bind(int source, int flags, intr_handler_t handler, void *arg, intr_handle_t shared_handle, intr_handle_t *ret_handle);
|
||||
|
||||
|
||||
/**
|
||||
* @brief Allocate an interrupt with the given parameters, including an interrupt status register, that can
|
||||
* be bound to an existing interrupt handler
|
||||
*
|
||||
*
|
||||
* This function does the same as esp_intr_alloc_intrstatus, but allows specifying a previously allocated handler as
|
||||
* the interrupt to share with the given source. This can be very handy to treat two pre-determined interrupt
|
||||
* sources in the same interrupt handler. The interrupt will be allocated on the same core as the given
|
||||
* `shared_handle`. Moreover, make sure to specify the same interrupt level as the one being used by `shared_handle`
|
||||
* to prevent any failure from this function.
|
||||
*
|
||||
* @param source The interrupt source. One of the ETS_*_INTR_SOURCE interrupt mux
|
||||
* sources, as defined in soc/soc.h, or one of the internal
|
||||
* ETS_INTERNAL_*_INTR_SOURCE sources as defined in this header.
|
||||
* @param flags An ORred mask of the ESP_INTR_FLAG_* defines. These restrict the
|
||||
* choice of interrupts that this routine can choose from. If this value
|
||||
* is 0, it will default to allocating a non-shared interrupt of level
|
||||
* 1, 2 or 3. If ESP_INTR_FLAG_SHARED mask is provided, a shared interrupt of
|
||||
* the given level will be allocated (or level 1 if not specified).
|
||||
* Setting ESP_INTR_FLAG_INTRDISABLED will return from this function with the
|
||||
* interrupt disabled.
|
||||
* @param intrstatusreg The address of an interrupt status register
|
||||
* @param intrstatusmask A mask. If a read of address intrstatusreg has any of the bits
|
||||
* that are 1 in the mask set, the ISR will be called. If not, it will be
|
||||
* skipped.
|
||||
* @param handler The interrupt handler. Must be NULL when an interrupt of level >3
|
||||
* is requested, because these types of interrupts aren't C-callable.
|
||||
* @param arg Optional argument for passed to the interrupt handler
|
||||
* @param shared_handle Previously allocated interrupt to share the CPU interrupt line with. If NULL,
|
||||
* calling this function equivalent to esp_intr_alloc, else, ESP_INTR_FLAG_SHARED must
|
||||
* be provided in the flags parameter.
|
||||
* @param ret_handle Pointer to an intr_handle_t to store a handle that can later be
|
||||
* used to request details or free the interrupt. Can be NULL if no handle
|
||||
* is required.
|
||||
*
|
||||
* @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid.
|
||||
* ESP_ERR_NOT_FOUND No free interrupt found with the specified flasg or the given level is different
|
||||
* from the one assigned to the share_handle parameter.
|
||||
* ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_intr_alloc_intrstatus_bind(int source, int flags, uint32_t intrstatusreg, uint32_t intrstatusmask, intr_handler_t handler,
|
||||
void *arg, intr_handle_t shared_handle, intr_handle_t *ret_handle);
|
||||
|
||||
/**
|
||||
* @brief Disable and free an interrupt.
|
||||
*
|
||||
@ -202,7 +285,7 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre
|
||||
*
|
||||
* @return ESP_ERR_INVALID_ARG the handle is NULL
|
||||
* ESP_FAIL failed to release this handle
|
||||
* ESP_OK otherwise
|
||||
* ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_intr_free(intr_handle_t handle);
|
||||
|
||||
@ -239,7 +322,7 @@ int esp_intr_get_intno(intr_handle_t handle);
|
||||
* @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus
|
||||
*
|
||||
* @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid.
|
||||
* ESP_OK otherwise
|
||||
* ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_intr_disable(intr_handle_t handle);
|
||||
|
||||
@ -252,7 +335,7 @@ esp_err_t esp_intr_disable(intr_handle_t handle);
|
||||
* @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus
|
||||
*
|
||||
* @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid.
|
||||
* ESP_OK otherwise
|
||||
* ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_intr_enable(intr_handle_t handle);
|
||||
|
||||
@ -266,7 +349,7 @@ esp_err_t esp_intr_enable(intr_handle_t handle);
|
||||
* Handlers residing in IRAM can be called when cache is disabled.
|
||||
*
|
||||
* @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid.
|
||||
* ESP_OK otherwise
|
||||
* ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_intr_set_in_iram(intr_handle_t handle, bool is_in_iram);
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2025 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@ -72,6 +72,16 @@ struct shared_vector_desc_t {
|
||||
#define VECDESC_FL_INIRAM (1<<1)
|
||||
#define VECDESC_FL_SHARED (1<<2)
|
||||
#define VECDESC_FL_NONSHARED (1<<3)
|
||||
#define VECDESC_FL_TYPE_MASK (0xf)
|
||||
|
||||
#if SOC_CPU_HAS_FLEXIBLE_INTC
|
||||
/* On targets that have configurable interrupts levels, store the assigned level in the flags */
|
||||
#define VECDESC_FL_LEVEL_SHIFT (8)
|
||||
/* Allocate 4 bits in the flag */
|
||||
#define VECDESC_FL_LEVEL_MASK (0xf)
|
||||
/* Help to extract the level from flags */
|
||||
#define VECDESC_FL_LEVEL(flags) (((flags) >> VECDESC_FL_LEVEL_SHIFT) & VECDESC_FL_LEVEL_MASK)
|
||||
#endif
|
||||
|
||||
//Pack using bitfields for better memory use
|
||||
struct vector_desc_t {
|
||||
@ -212,7 +222,7 @@ esp_err_t esp_intr_mark_shared(int intno, int cpu, bool is_int_ram)
|
||||
portEXIT_CRITICAL(&spinlock);
|
||||
return ESP_ERR_NO_MEM;
|
||||
}
|
||||
vd->flags = VECDESC_FL_SHARED;
|
||||
vd->flags = (vd->flags & ~VECDESC_FL_TYPE_MASK) | VECDESC_FL_SHARED;
|
||||
if (is_int_ram) {
|
||||
vd->flags |= VECDESC_FL_INIRAM;
|
||||
}
|
||||
@ -258,7 +268,17 @@ static bool is_vect_desc_usable(vector_desc_t *vd, int flags, int cpu, int force
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifndef SOC_CPU_HAS_FLEXIBLE_INTC
|
||||
#if SOC_CPU_HAS_FLEXIBLE_INTC
|
||||
/* On target that have configurable interrupts levels, check if the interrupt has already
|
||||
* been allocated, and if yes, make sure the levels are compatible. */
|
||||
const int vector_lvl = VECDESC_FL_LEVEL(vd->flags);
|
||||
/* A non-zero value means the level has already been set prior to this allocation, make
|
||||
* sure the current level matches what we need. */
|
||||
if (vector_lvl != 0 && (flags & (1 << vector_lvl)) == 0) {
|
||||
ALCHLOG("....Unusable: incompatible priority");
|
||||
return false;
|
||||
}
|
||||
#else
|
||||
//Check if the interrupt priority is acceptable
|
||||
if (!(flags & (1 << intr_desc.priority))) {
|
||||
ALCHLOG("....Unusable: incompatible priority");
|
||||
@ -480,8 +500,8 @@ bool esp_intr_ptr_in_isr_region(void* ptr)
|
||||
|
||||
|
||||
//We use ESP_EARLY_LOG* here because this can be called before the scheduler is running.
|
||||
esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusreg, uint32_t intrstatusmask, intr_handler_t handler,
|
||||
void *arg, intr_handle_t *ret_handle)
|
||||
esp_err_t esp_intr_alloc_intrstatus_bind(int source, int flags, uint32_t intrstatusreg, uint32_t intrstatusmask, intr_handler_t handler,
|
||||
void *arg, intr_handle_t shared_handle, intr_handle_t *ret_handle)
|
||||
{
|
||||
intr_handle_data_t *ret=NULL;
|
||||
int force = -1;
|
||||
@ -509,7 +529,10 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre
|
||||
if ((flags & ESP_INTR_FLAG_IRAM) && handler && !esp_intr_ptr_in_isr_region(handler)) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
|
||||
//Shared handler must be passed with share interrupt flag
|
||||
if (shared_handle != NULL && (flags & ESP_INTR_FLAG_SHARED) == 0) {
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
//Default to prio 1 for shared interrupts. Default to prio 1, 2 or 3 for non-shared interrupts.
|
||||
if ((flags & ESP_INTR_FLAG_LEVELMASK) == 0) {
|
||||
if (flags & ESP_INTR_FLAG_SHARED) {
|
||||
@ -549,7 +572,17 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre
|
||||
|
||||
portENTER_CRITICAL(&spinlock);
|
||||
uint32_t cpu = esp_cpu_get_core_id();
|
||||
//See if we can find an interrupt that matches the flags.
|
||||
if (shared_handle != NULL) {
|
||||
/* Sanity check, should not occur */
|
||||
if (shared_handle->vector_desc == NULL) {
|
||||
portEXIT_CRITICAL(&spinlock);
|
||||
return ESP_ERR_INVALID_ARG;
|
||||
}
|
||||
/* If a shared vector was given, force the current interrupt source to same CPU interrupt line */
|
||||
force = shared_handle->vector_desc->intno;
|
||||
/* Allocate the interrupt on the same core as the given handle */
|
||||
cpu = shared_handle->vector_desc->cpu;
|
||||
}
|
||||
int intr = get_available_int(flags, cpu, force, source);
|
||||
if (intr == -1) {
|
||||
//None found. Bail out.
|
||||
@ -640,6 +673,7 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre
|
||||
#if SOC_CPU_HAS_FLEXIBLE_INTC
|
||||
//Extract the level from the interrupt passed flags
|
||||
int level = esp_intr_flags_to_level(flags);
|
||||
vd->flags |= level << VECDESC_FL_LEVEL_SHIFT;
|
||||
esp_cpu_intr_set_priority(intr, level);
|
||||
|
||||
if (flags & ESP_INTR_FLAG_EDGE) {
|
||||
@ -671,6 +705,13 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusreg, uint32_t intrstatusmask, intr_handler_t handler,
|
||||
void *arg, intr_handle_t *ret_handle)
|
||||
{
|
||||
return esp_intr_alloc_intrstatus_bind(source, flags, intrstatusreg, intrstatusmask, handler, arg, NULL, ret_handle);
|
||||
}
|
||||
|
||||
|
||||
esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *arg, intr_handle_t *ret_handle)
|
||||
{
|
||||
/*
|
||||
@ -681,6 +722,13 @@ esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *ar
|
||||
return esp_intr_alloc_intrstatus(source, flags, 0, 0, handler, arg, ret_handle);
|
||||
}
|
||||
|
||||
|
||||
esp_err_t esp_intr_alloc_bind(int source, int flags, intr_handler_t handler, void *arg, intr_handle_t shared_handle, intr_handle_t *ret_handle)
|
||||
{
|
||||
return esp_intr_alloc_intrstatus_bind(source, flags, 0, 0, handler, arg, shared_handle, ret_handle);
|
||||
}
|
||||
|
||||
|
||||
esp_err_t IRAM_ATTR esp_intr_set_in_iram(intr_handle_t handle, bool is_in_iram)
|
||||
{
|
||||
if (!handle) {
|
||||
@ -790,6 +838,10 @@ static esp_err_t intr_free_for_current_cpu(intr_handle_t handle)
|
||||
//we save.(We can also not use the same exit path for empty shared ints anymore if we delete
|
||||
//the desc.) For now, just mark it as free.
|
||||
handle->vector_desc->flags &= ~(VECDESC_FL_NONSHARED|VECDESC_FL_RESERVED|VECDESC_FL_SHARED);
|
||||
#if SOC_CPU_HAS_FLEXIBLE_INTC
|
||||
//Clear the assigned level
|
||||
handle->vector_desc->flags &= ~(VECDESC_FL_LEVEL_MASK << VECDESC_FL_LEVEL_SHIFT);
|
||||
#endif
|
||||
handle->vector_desc->source = ETS_INTERNAL_UNUSED_INTR_SOURCE;
|
||||
|
||||
//Also kill non_iram mask bit.
|
||||
@ -802,11 +854,17 @@ static esp_err_t intr_free_for_current_cpu(intr_handle_t handle)
|
||||
|
||||
int esp_intr_get_intno(intr_handle_t handle)
|
||||
{
|
||||
if (handle == NULL || handle->vector_desc == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return handle->vector_desc->intno;
|
||||
}
|
||||
|
||||
int esp_intr_get_cpu(intr_handle_t handle)
|
||||
{
|
||||
if (handle == NULL || handle->vector_desc == NULL) {
|
||||
return -1;
|
||||
}
|
||||
return handle->vector_desc->cpu;
|
||||
}
|
||||
|
||||
|
@ -22,7 +22,7 @@ if(CONFIG_SOC_ETM_SUPPORTED)
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_GPTIMER_SUPPORTED)
|
||||
list(APPEND SRC "test_intr_alloc.c")
|
||||
list(APPEND srcs "test_intr_alloc.c")
|
||||
endif()
|
||||
|
||||
# In order for the cases defined by `TEST_CASE` to be linked into the final elf,
|
||||
|
@ -66,7 +66,7 @@ static void timer_test(int flags)
|
||||
}
|
||||
|
||||
if ((flags & ESP_INTR_FLAG_SHARED)) {
|
||||
/* Check that the allocated interrupts are acutally shared */
|
||||
/* Check that the allocated interrupts are actually shared */
|
||||
int intr_num = esp_intr_get_intno(inth[0]);
|
||||
for (int i = 0; i < SOC_TIMER_GROUP_TOTAL_TIMERS; i++) {
|
||||
TEST_ASSERT_EQUAL(intr_num, esp_intr_get_intno(inth[i]));
|
||||
@ -124,6 +124,106 @@ void static test_isr(void*arg)
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("Intr_alloc test, shared interrupts don't affect level", "[intr_alloc]")
|
||||
{
|
||||
intr_handle_t handle_lvl_1;
|
||||
intr_handle_t handle_lvl_2;
|
||||
|
||||
/* Allocate an interrupt of level 1 that will be shared with another source */
|
||||
esp_err_t err = esp_intr_alloc(ETS_FROM_CPU_INTR2_SOURCE,
|
||||
ESP_INTR_FLAG_LEVEL1 | ESP_INTR_FLAG_SHARED,
|
||||
test_isr, NULL, &handle_lvl_1);
|
||||
TEST_ESP_OK(err);
|
||||
|
||||
/* Allocate a shared interrupt of a different level */
|
||||
err = esp_intr_alloc(ETS_FROM_CPU_INTR3_SOURCE,
|
||||
ESP_INTR_FLAG_LEVEL2 | ESP_INTR_FLAG_SHARED,
|
||||
test_isr, NULL, &handle_lvl_2);
|
||||
TEST_ESP_OK(err);
|
||||
|
||||
/* Make sure the allocated CPU line is NOT the same for both sources */
|
||||
const int intlvl1 = esp_intr_get_intno(handle_lvl_1);
|
||||
const int intlvl2 = esp_intr_get_intno(handle_lvl_2);
|
||||
printf("Level 1 interrupt allocated: %d\n", intlvl1);
|
||||
printf("Level 2 interrupt allocated: %d\n", intlvl2);
|
||||
TEST_ASSERT(intlvl1 != intlvl2);
|
||||
|
||||
TEST_ESP_OK(esp_intr_free(handle_lvl_1));
|
||||
TEST_ESP_OK(esp_intr_free(handle_lvl_2));
|
||||
}
|
||||
|
||||
|
||||
#if SOC_CPU_HAS_FLEXIBLE_INTC
|
||||
|
||||
/**
|
||||
* On targets that have flexible interrupt levels, make sure that a shared interrupt sees its level
|
||||
* being cleared (and reconfigurable) uupon remove and reallocation.
|
||||
*/
|
||||
TEST_CASE("Intr_alloc test, shared interrupts custom level cleared", "[intr_alloc]")
|
||||
{
|
||||
intr_handle_t handle;
|
||||
|
||||
esp_err_t err = esp_intr_alloc(ETS_FROM_CPU_INTR2_SOURCE,
|
||||
ESP_INTR_FLAG_LEVEL1 | ESP_INTR_FLAG_SHARED,
|
||||
test_isr, NULL, &handle);
|
||||
TEST_ESP_OK(err);
|
||||
const int first_intno = esp_intr_get_intno(handle);
|
||||
/* Make sure the priority is correct */
|
||||
TEST_ASSERT_EQUAL(1, esp_cpu_intr_get_priority(first_intno));
|
||||
|
||||
/* Free the shared interrupt and try to reallocate it with another level */
|
||||
TEST_ESP_OK(esp_intr_free(handle));
|
||||
|
||||
err = esp_intr_alloc(ETS_FROM_CPU_INTR3_SOURCE,
|
||||
ESP_INTR_FLAG_LEVEL2 | ESP_INTR_FLAG_SHARED,
|
||||
test_isr, NULL, &handle);
|
||||
TEST_ESP_OK(err);
|
||||
|
||||
/* Make sure they are both the same and the level has been updated */
|
||||
const int second_intno = esp_intr_get_intno(handle);
|
||||
TEST_ASSERT_EQUAL(2, esp_cpu_intr_get_priority(second_intno));
|
||||
TEST_ASSERT(first_intno == second_intno);
|
||||
|
||||
/* Delete the interrupt */
|
||||
TEST_ESP_OK(esp_intr_free(handle));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
/**
|
||||
* Make sure we can map two given sources to the same interrupt line when their levels match.
|
||||
*/
|
||||
TEST_CASE("Intr_alloc test, shared interrupt line for two sources", "[intr_alloc]")
|
||||
{
|
||||
intr_handle_t handle_1;
|
||||
intr_handle_t handle_2;
|
||||
|
||||
esp_err_t err = esp_intr_alloc(ETS_FROM_CPU_INTR2_SOURCE,
|
||||
ESP_INTR_FLAG_LEVEL1 | ESP_INTR_FLAG_SHARED,
|
||||
test_isr, NULL, &handle_1);
|
||||
TEST_ESP_OK(err);
|
||||
|
||||
/* Map another source to the exact same interrupt line */
|
||||
err = esp_intr_alloc_bind(ETS_FROM_CPU_INTR3_SOURCE,
|
||||
ESP_INTR_FLAG_LEVEL1 | ESP_INTR_FLAG_SHARED,
|
||||
test_isr, NULL, handle_1, &handle_2);
|
||||
TEST_ESP_OK(err);
|
||||
/* Make sure they are both using the same interrupt line */
|
||||
TEST_ASSERT_EQUAL(esp_intr_get_intno(handle_1), esp_intr_get_intno(handle_2));
|
||||
|
||||
/* Reallocate the second interrupt source with a higher level, it must fail */
|
||||
TEST_ESP_OK(esp_intr_free(handle_2));
|
||||
err = esp_intr_alloc_bind(ETS_FROM_CPU_INTR3_SOURCE,
|
||||
ESP_INTR_FLAG_LEVEL2 | ESP_INTR_FLAG_SHARED,
|
||||
test_isr, NULL, handle_1, &handle_2);
|
||||
TEST_ASSERT(err != ESP_OK);
|
||||
|
||||
/* Free the remaining handler */
|
||||
TEST_ESP_OK(esp_intr_free(handle_1));
|
||||
}
|
||||
|
||||
|
||||
TEST_CASE("Allocate previously freed interrupt, with different flags", "[intr_alloc]")
|
||||
{
|
||||
intr_handle_t intr;
|
||||
|
@ -32,7 +32,7 @@ Overview
|
||||
|
||||
Because there are more interrupt sources than interrupts, sometimes it makes sense to share an interrupt in multiple drivers. The :cpp:func:`esp_intr_alloc` abstraction exists to hide all these implementation details.
|
||||
|
||||
A driver can allocate an interrupt for a certain peripheral by calling :cpp:func:`esp_intr_alloc` (or :cpp:func:`esp_intr_alloc_intrstatus`). It can use the flags passed to this function to specify the type, priority, and trigger method of the interrupt to allocate. The interrupt allocation code will then find an applicable interrupt, use the interrupt matrix to hook it up to the peripheral, and install the given interrupt handler and ISR to it.
|
||||
A driver can allocate an interrupt for a certain peripheral by calling :cpp:func:`esp_intr_alloc`, :cpp:func:`esp_intr_alloc_bind`, :cpp:func:`esp_intr_alloc_intrstatus`, or :cpp:func:`esp_intr_alloc_intrstatus_bind`. It can use the flags passed to this function to specify the type, priority, and trigger method of the interrupt to allocate. The interrupt allocation code will then find an applicable interrupt, use the interrupt matrix to hook it up to the peripheral, and install the given interrupt handler and ISR to it.
|
||||
|
||||
The interrupt allocator presents two different types of interrupts, namely shared interrupts and non-shared interrupts, both of which require different handling. Non-shared interrupts will allocate a separate interrupt for every :cpp:func:`esp_intr_alloc` call, and this interrupt is use solely for the peripheral attached to it, with only one ISR that will get called. Shared interrupts can have multiple peripherals triggering them, with multiple ISRs being called when one of the peripherals attached signals an interrupt. Thus, ISRs that are intended for shared interrupts should check the interrupt status of the peripheral they service in order to check if any action is required.
|
||||
|
||||
@ -138,9 +138,7 @@ Several handlers can be assigned to a same source, given that all handlers are a
|
||||
|
||||
Sources attached to non-shared interrupt do not support this feature.
|
||||
|
||||
.. only:: not SOC_CPU_HAS_FLEXIBLE_INTC
|
||||
|
||||
By default, when ``ESP_INTR_FLAG_SHARED`` flag is specified, the interrupt allocator will allocate only priority level 1 interrupts. Use ``ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_LOWMED`` to also allow allocating shared interrupts at priority levels 2 and 3.
|
||||
By default, when ``ESP_INTR_FLAG_SHARED`` flag is specified, the interrupt allocator will allocate only priority level 1 interrupts. Use ``ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_LOWMED`` to also allow allocating shared interrupts at priority levels 2 and 3.
|
||||
|
||||
Though the framework supports this feature, you have to use it **very carefully**. There usually exist two ways to stop an interrupt from being triggered: **disable the source** or **mask peripheral interrupt status**. ESP-IDF only handles enabling and disabling of the source itself, leaving status and mask bits to be handled by users.
|
||||
|
||||
@ -150,6 +148,8 @@ Though the framework supports this feature, you have to use it **very carefully*
|
||||
|
||||
Leaving some status bits unhandled without masking them, while disabling the handlers for them, will cause the interrupt(s) to be triggered indefinitely, resulting therefore in a system crash.
|
||||
|
||||
When calling :cpp:func:`esp_intr_alloc` or :cpp:func:`esp_intr_alloc_intrstatus`, the interrupt allocator selects the first interrupt that meets the level requirements for mapping the specified source, without considering other sources already mapped to the shared interrupt line. However, by using the functions :cpp:func:`esp_intr_alloc_bind` or :cpp:func:`esp_intr_alloc_intrstatus_bind`, you can explicitly specify the interrupt handler to be shared with the given interrupt source.
|
||||
|
||||
|
||||
Troubleshooting Interrupt Allocation
|
||||
------------------------------------
|
||||
|
@ -8,31 +8,31 @@
|
||||
|
||||
.. only:: esp32 or esp32s3
|
||||
|
||||
{IDF_TARGET_NAME} 有两个核,每个核有 32 个中断。每个中断都有一个确定的优先级别,大多数中断(但不是全部)都连接到中断矩阵。
|
||||
{IDF_TARGET_NAME} 有两个核,每个核有 32 个中断。每个中断都有一个确定的优先级别,大多数中断(但不是全部)都连接到中断矩阵。
|
||||
|
||||
.. only:: esp32s2
|
||||
|
||||
{IDF_TARGET_NAME} 有一个核,32 个中断。每个中断都有一个确定的优先级别,大多数中断(但不是全部)都连接到中断矩阵。
|
||||
{IDF_TARGET_NAME} 有一个核,32 个中断。每个中断都有一个确定的优先级别,大多数中断(但不是全部)都连接到中断矩阵。
|
||||
|
||||
.. only:: esp32c2 or esp32c3
|
||||
|
||||
{IDF_TARGET_NAME} 有一个核,31 个中断。每个中断的优先级别都可独立地通过编程设置。
|
||||
{IDF_TARGET_NAME} 有一个核,31 个中断。每个中断的优先级别都可独立地通过编程设置。
|
||||
|
||||
.. only:: esp32c6 or esp32h2
|
||||
|
||||
{IDF_TARGET_NAME} 有一个核,28 个外部异步中断。每个中断的优先级别都可独立地通过编程设置。此外,还有 4 个核心本地中断源 (CLINT)。详细信息请参见 **{IDF_TARGET_NAME} 技术参考手册** [`PDF <{IDF_TARGET_TRM_CN_URL}#riscvcpu>`__]。
|
||||
{IDF_TARGET_NAME} 有一个核,28 个外部异步中断。每个中断的优先级别都可独立地通过编程设置。此外,还有 4 个核心本地中断源 (CLINT)。详细信息请参见 **{IDF_TARGET_NAME} 技术参考手册** [`PDF <{IDF_TARGET_TRM_CN_URL}#riscvcpu>`__]。
|
||||
|
||||
.. only:: esp32p4
|
||||
|
||||
{IDF_TARGET_NAME} 有两个核,每个核有 32 个外部异步中断。每个中断的优先级别都可独立地通过编程设置。此外,每个核还有 3 个核心本地中断源 (CLINT)。详细信息请参见 **{IDF_TARGET_NAME} 技术参考手册** [`PDF <{IDF_TARGET_TRM_CN_URL}#riscvcpu>`__]。
|
||||
{IDF_TARGET_NAME} 有两个核,每个核有 32 个外部异步中断。每个中断的优先级别都可独立地通过编程设置。此外,每个核还有 3 个核心本地中断源 (CLINT)。详细信息请参见 **{IDF_TARGET_NAME} 技术参考手册** [`PDF <{IDF_TARGET_TRM_CN_URL}#riscvcpu>`__]。
|
||||
|
||||
.. only:: esp32c5 or esp32c61
|
||||
|
||||
{IDF_TARGET_NAME} 有一个核,32 个外部异步中断。每个中断的优先级别都可独立地通过编程设置。此外,还有 3 个核心本地中断源 (CLINT)。详细信息请参见 **{IDF_TARGET_NAME} 技术参考手册** > **高性能处理器** [`PDF <{IDF_TARGET_TRM_CN_URL}#riscvcpu>`__]。
|
||||
{IDF_TARGET_NAME} 有一个核,32 个外部异步中断。每个中断的优先级别都可独立地通过编程设置。此外,还有 3 个核心本地中断源 (CLINT)。详细信息请参见 **{IDF_TARGET_NAME} 技术参考手册** > **高性能处理器** [`PDF <{IDF_TARGET_TRM_CN_URL}#riscvcpu>`__]。
|
||||
|
||||
由于中断源数量多于中断,有时多个驱动程序可以共用一个中断。:cpp:func:`esp_intr_alloc` 抽象隐藏了这些实现细节。
|
||||
|
||||
驱动程序可以通过调用 :cpp:func:`esp_intr_alloc`,或 :cpp:func:`esp_intr_alloc_intrstatus` 为某个外设分配中断。通过向此函数传递 flag,可以指定中断类型、优先级和触发方式。然后,中断分配代码会找到适用的中断,使用中断矩阵将其连接到外设,并为其安装给定的中断处理程序和 ISR。
|
||||
驱动程序可以通过调用 :cpp:func:`esp_intr_alloc`、:cpp:func:`esp_intr_alloc_bind`、 :cpp:func:`esp_intr_alloc_intrstatus` 或 :cpp:func:`esp_intr_alloc_intrstatus_bind` 为某个外设分配中断。通过向此函数传递 flag,可以指定中断类型、优先级和触发方式。然后,中断分配代码会找到适用的中断,使用中断矩阵将其连接到外设,并为其安装给定的中断处理程序和 ISR。
|
||||
|
||||
中断分配器提供两种不同的中断类型:共享中断和非共享中断,这两种中断需要不同处理方式。非共享中断在每次调用 :cpp:func:`esp_intr_alloc` 时,都会分配一个单独的中断,该中断仅用于与其相连的外设,只调用一个 ISR。共享中断则可以由多个外设触发,当其中一个外设发出中断信号时,会调用多个 ISR。因此,针对共享中断的 ISR 应检查对应外设的中断状态,以确定是否需要采取任何操作。
|
||||
|
||||
@ -138,9 +138,7 @@ IRAM 安全中断处理程序
|
||||
|
||||
关联到非共享中断的源不支持此功能。
|
||||
|
||||
.. only:: not SOC_CPU_HAS_FLEXIBLE_INTC
|
||||
|
||||
默认情况下,指定 ``ESP_INTR_FLAG_SHARED`` flag 时,中断分配器仅分配优先级为 1 的中断。可以使用 ``ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_LOWMED`` 允许分配优先级为 2 和 3 的共享中断。
|
||||
默认情况下,指定 ``ESP_INTR_FLAG_SHARED`` flag 时,中断分配器仅分配优先级为 1 的中断。可以使用 ``ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_LOWMED`` 允许分配优先级为 2 和 3 的共享中断。
|
||||
|
||||
尽管支持此功能,使用时也必须 **非常小心**。通常存在两种办法可以阻止中断触发: **禁用源** 或 **屏蔽外设中断状态**。ESP-IDF 仅处理源本身的启用和禁用,中断源的状态位和屏蔽位须由用户操作。
|
||||
|
||||
@ -150,6 +148,8 @@ IRAM 安全中断处理程序
|
||||
|
||||
如果不屏蔽状态位而让其处于未处理状态,同时禁用这些状态位的处理程序,就会导致无限次触发中断,引起系统崩溃。
|
||||
|
||||
调用 :cpp:func:`esp_intr_alloc` 或 :cpp:func:`esp_intr_alloc_intrstatus` 时,中断分配器会选择第一个满足电平要求的中断为指定的源映射中断,而不会考虑已经映射到共享中断线上的其他源。然而,通过使用 :cpp:func:`esp_intr_alloc_bind` 或 :cpp:func:`esp_intr_alloc_intrstatus_bind` 函数,可以显式地指定中断处理程序与给定的中断源共享。
|
||||
|
||||
|
||||
排除中断分配故障
|
||||
------------------
|
||||
|
@ -36,8 +36,8 @@ if(NOT CONFIG_TEST_MEMPROT AND NOT CONFIG_ESP_COREDUMP_CAPTURE_DRAM)
|
||||
esp_common esp_hw_support soc hal freertos)
|
||||
if(CONFIG_ESP_COREDUMP_CHECKSUM_SHA256)
|
||||
if(CONFIG_IDF_TARGET_ESP32S2)
|
||||
# due to the ram limitation, coredump is removed from esp32s2 built
|
||||
list(REMOVE_ITEM ubsan_components espcoredump)
|
||||
# due to the ram limitation, coredump and freertos are removed from esp32s2 built
|
||||
list(REMOVE_ITEM ubsan_components espcoredump freertos)
|
||||
endif()
|
||||
endif()
|
||||
foreach(component IN LISTS ubsan_components)
|
||||
|
Loading…
x
Reference in New Issue
Block a user