diff --git a/components/esp32/CMakeLists.txt b/components/esp32/CMakeLists.txt index bf7925fd18..4253793ea7 100644 --- a/components/esp32/CMakeLists.txt +++ b/components/esp32/CMakeLists.txt @@ -19,7 +19,6 @@ else() "dport_access.c" "esp_himem.c" "hw_random.c" - "intr_alloc.c" "spiram.c" "spiram_psram.c" "system_api_esp32.c") diff --git a/components/esp32s2/CMakeLists.txt b/components/esp32s2/CMakeLists.txt index 3acbc63ae1..3c27672efa 100644 --- a/components/esp32s2/CMakeLists.txt +++ b/components/esp32s2/CMakeLists.txt @@ -17,7 +17,6 @@ else() "crosscore_int.c" "dport_access.c" "hw_random.c" - "intr_alloc.c" "spiram.c" "spiram_psram.c" "system_api_esp32s2.c" diff --git a/components/esp32s2/include/esp_intr_alloc.h b/components/esp32s2/include/esp_intr_alloc.h deleted file mode 100644 index 9b0b3c394f..0000000000 --- a/components/esp32s2/include/esp_intr_alloc.h +++ /dev/null @@ -1,305 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef __ESP_INTR_ALLOC_H__ -#define __ESP_INTR_ALLOC_H__ - -#include -#include -#include "esp_err.h" -#include "freertos/xtensa_api.h" - -#ifdef __cplusplus -extern "C" { -#endif - - -/** @addtogroup Intr_Alloc - * @{ - */ - - -/** @brief Interrupt allocation flags - * - * These flags can be used to specify which interrupt qualities the - * code calling esp_intr_alloc* needs. - * - */ - -//Keep the LEVELx values as they are here; they match up with (1<3 - * is requested, because these types of interrupts aren't C-callable. - * @param arg Optional argument for passed to the interrupt handler - * @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 flags - * ESP_OK otherwise - */ -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. - * - * - * This essentially does the same as esp_intr_alloc, but allows specifying a register and mask - * combo. For shared interrupts, the handler is only called if a read from the specified - * register, ANDed with the mask, returns non-zero. By passing an interrupt status register - * address and a fitting mask, this can be used to accelerate interrupt handling in the case - * a shared interrupt is triggered; by checking the interrupt statuses first, the code can - * decide which ISRs can be skipped - * - * @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 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. - * @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 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 flags - * ESP_OK otherwise - */ -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 Disable and free an interrupt. - * - * Use an interrupt handle to disable the interrupt and release the resources - * associated with it. - * - * @note - * When the handler shares its source with other handlers, the interrupt status - * bits it's responsible for should be managed properly before freeing it. see - * ``esp_intr_disable`` for more details. - * - * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus - * - * @return ESP_ERR_INVALID_ARG if handle is invalid, or esp_intr_free runs on another core than - * where the interrupt is allocated on. - * ESP_OK otherwise - */ -esp_err_t esp_intr_free(intr_handle_t handle); - - -/** - * @brief Get CPU number an interrupt is tied to - * - * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus - * - * @return The core number where the interrupt is allocated - */ -int esp_intr_get_cpu(intr_handle_t handle); - -/** - * @brief Get the allocated interrupt for a certain handle - * - * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus - * - * @return The interrupt number - */ -int esp_intr_get_intno(intr_handle_t handle); - -/** - * @brief Disable the interrupt associated with the handle - * - * @note - * 1. For local interrupts (ESP_INTERNAL_* sources), this function has to be called on the - * CPU the interrupt is allocated on. Other interrupts have no such restriction. - * 2. When several handlers sharing a same interrupt source, interrupt status bits, which are - * handled in the handler to be disabled, should be masked before the disabling, or handled - * in other enabled interrupts properly. Miss of interrupt status handling will cause infinite - * interrupt calls and finally system crash. - * - * @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_err_t esp_intr_disable(intr_handle_t handle); - -/** - * @brief Enable the interrupt associated with the handle - * - * @note For local interrupts (ESP_INTERNAL_* sources), this function has to be called on the - * CPU the interrupt is allocated on. Other interrupts have no such restriction. - * - * @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_err_t esp_intr_enable(intr_handle_t handle); - -/** - * @brief Set the "in IRAM" status of the handler. - * - * @note Does not work on shared interrupts. - * - * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus - * @param is_in_iram Whether the handler associated with this handle resides in IRAM. - * 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_err_t esp_intr_set_in_iram(intr_handle_t handle, bool is_in_iram); - -/** - * @brief Disable interrupts that aren't specifically marked as running from IRAM - */ -void esp_intr_noniram_disable(void); - - -/** - * @brief Re-enable interrupts disabled by esp_intr_noniram_disable - */ -void esp_intr_noniram_enable(void); - -/**@}*/ - - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/components/esp32s2/intr_alloc.c b/components/esp32s2/intr_alloc.c deleted file mode 100644 index 066472744e..0000000000 --- a/components/esp32s2/intr_alloc.c +++ /dev/null @@ -1,894 +0,0 @@ -// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at - -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - - - -#include "sdkconfig.h" -#include -#include -#include -#include -#include -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include -#include "esp_err.h" -#include "esp_log.h" -#include "esp_intr_alloc.h" -#include "esp_attr.h" -#include "soc/soc.h" -#include -#include - -static const char* TAG = "intr_alloc"; - -#define ETS_INTERNAL_TIMER0_INTR_NO 6 -#define ETS_INTERNAL_TIMER1_INTR_NO 15 -#define ETS_INTERNAL_TIMER2_INTR_NO 16 -#define ETS_INTERNAL_SW0_INTR_NO 7 -#define ETS_INTERNAL_SW1_INTR_NO 29 -#define ETS_INTERNAL_PROFILING_INTR_NO 11 - - -/* -Define this to debug the choices made when allocating the interrupt. This leads to much debugging -output within a critical region, which can lead to weird effects like e.g. the interrupt watchdog -being triggered, that is why it is separate from the normal LOG* scheme. -*/ -//define DEBUG_INT_ALLOC_DECISIONS -#ifdef DEBUG_INT_ALLOC_DECISIONS -# define ALCHLOG(...) ESP_EARLY_LOGD(TAG, __VA_ARGS__) -#else -# define ALCHLOG(...) do {} while (0) -#endif - - -typedef enum { - INTDESC_NORMAL=0, - INTDESC_RESVD, - INTDESC_SPECIAL //for xtensa timers / software ints -} int_desc_flag_t; - -typedef enum { - INTTP_LEVEL=0, - INTTP_EDGE, - INTTP_NA -} int_type_t; - -typedef struct { - int level; - int_type_t type; - int_desc_flag_t cpuflags[2]; -} int_desc_t; - - -//We should mark the interrupt for the timer used by FreeRTOS as reserved. The specific timer -//is selectable using menuconfig; we use these cpp bits to convert that into something we can use in -//the table below. -#if CONFIG_FREERTOS_CORETIMER_0 -#define INT6RES INTDESC_RESVD -#else -#define INT6RES INTDESC_SPECIAL -#endif - -#if CONFIG_FREERTOS_CORETIMER_1 -#define INT15RES INTDESC_RESVD -#else -#define INT15RES INTDESC_SPECIAL -#endif - -//This is basically a software-readable version of the interrupt usage table in include/soc/soc.h -const static int_desc_t int_desc[32]={ - { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //0 - { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //1 - { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //2 - { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //3 - { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_NORMAL} }, //4 - { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //5 - { 1, INTTP_NA, {INT6RES, INT6RES } }, //6 - { 1, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL}}, //7 - { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //8 - { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //9 - { 1, INTTP_EDGE , {INTDESC_NORMAL, INTDESC_NORMAL} }, //10 - { 3, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL}}, //11 - { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //12 - { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //13 - { 7, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //14, NMI - { 3, INTTP_NA, {INT15RES, INT15RES } }, //15 - { 5, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL} }, //16 - { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //17 - { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //18 - { 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //19 - { 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //20 - { 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //21 - { 3, INTTP_EDGE, {INTDESC_RESVD, INTDESC_NORMAL} }, //22 - { 3, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //23 - { 4, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_NORMAL} }, //24 - { 4, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //25 - { 5, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_RESVD } }, //26 - { 3, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //27 - { 4, INTTP_EDGE, {INTDESC_NORMAL, INTDESC_NORMAL} }, //28 - { 3, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL}}, //29 - { 4, INTTP_EDGE, {INTDESC_RESVD, INTDESC_RESVD } }, //30 - { 5, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //31 -}; - -typedef struct shared_vector_desc_t shared_vector_desc_t; -typedef struct vector_desc_t vector_desc_t; - -struct shared_vector_desc_t { - int disabled: 1; - int source: 8; - volatile uint32_t *statusreg; - uint32_t statusmask; - intr_handler_t isr; - void *arg; - shared_vector_desc_t *next; -}; - - -#define VECDESC_FL_RESERVED (1<<0) -#define VECDESC_FL_INIRAM (1<<1) -#define VECDESC_FL_SHARED (1<<2) -#define VECDESC_FL_NONSHARED (1<<3) - -//Pack using bitfields for better memory use -struct vector_desc_t { - int flags: 16; //OR of VECDESC_FLAG_* defines - unsigned int cpu: 1; - unsigned int intno: 5; - int source: 8; //Interrupt mux flags, used when not shared - shared_vector_desc_t *shared_vec_info; //used when VECDESC_FL_SHARED - vector_desc_t *next; -}; - -struct intr_handle_data_t { - vector_desc_t *vector_desc; - shared_vector_desc_t *shared_vector_desc; -}; - -typedef struct non_shared_isr_arg_t non_shared_isr_arg_t; - -struct non_shared_isr_arg_t { - intr_handler_t isr; - void *isr_arg; - int source; -}; - -//Linked list of vector descriptions, sorted by cpu.intno value -static vector_desc_t *vector_desc_head = NULL; - -//This bitmask has an 1 if the int should be disabled when the flash is disabled. -static uint32_t non_iram_int_mask[portNUM_PROCESSORS]; -//This bitmask has 1 in it if the int was disabled using esp_intr_noniram_disable. -static uint32_t non_iram_int_disabled[portNUM_PROCESSORS]; -static bool non_iram_int_disabled_flag[portNUM_PROCESSORS]; - -#if CONFIG_SYSVIEW_ENABLE -extern uint32_t port_switch_flag[]; -#endif - -static portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED; - -//Inserts an item into vector_desc list so that the list is sorted -//with an incrementing cpu.intno value. -static void insert_vector_desc(vector_desc_t *to_insert) -{ - vector_desc_t *vd=vector_desc_head; - vector_desc_t *prev=NULL; - while(vd!=NULL) { - if (vd->cpu > to_insert->cpu) break; - if (vd->cpu == to_insert->cpu && vd->intno >= to_insert->intno) break; - prev=vd; - vd=vd->next; - } - if ((vector_desc_head==NULL) || (prev==NULL)) { - //First item - to_insert->next = vd; - vector_desc_head=to_insert; - } else { - prev->next=to_insert; - to_insert->next=vd; - } -} - -//Returns a vector_desc entry for an intno/cpu, or NULL if none exists. -static vector_desc_t *find_desc_for_int(int intno, int cpu) -{ - vector_desc_t *vd=vector_desc_head; - while(vd!=NULL) { - if (vd->cpu==cpu && vd->intno==intno) break; - vd=vd->next; - } - return vd; -} - -//Returns a vector_desc entry for an intno/cpu. -//Either returns a preexisting one or allocates a new one and inserts -//it into the list. Returns NULL on malloc fail. -static vector_desc_t *get_desc_for_int(int intno, int cpu) -{ - vector_desc_t *vd=find_desc_for_int(intno, cpu); - if (vd==NULL) { - vector_desc_t *newvd=heap_caps_malloc(sizeof(vector_desc_t), MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT); - if (newvd==NULL) return NULL; - memset(newvd, 0, sizeof(vector_desc_t)); - newvd->intno=intno; - newvd->cpu=cpu; - insert_vector_desc(newvd); - return newvd; - } else { - return vd; - } -} - -//Returns a vector_desc entry for an source, the cpu parameter is used to tell GPIO_INT and GPIO_NMI from different CPUs -static vector_desc_t * find_desc_for_source(int source, int cpu) -{ - vector_desc_t *vd=vector_desc_head; - while(vd!=NULL) { - if ( !(vd->flags & VECDESC_FL_SHARED) ) { - if ( vd->source == source && cpu == vd->cpu ) break; - } else if ( vd->cpu == cpu ) { - // check only shared vds for the correct cpu, otherwise skip - bool found = false; - shared_vector_desc_t *svd = vd->shared_vec_info; - assert(svd != NULL ); - while(svd) { - if ( svd->source == source ) { - found = true; - break; - } - svd = svd->next; - } - if ( found ) break; - } - vd=vd->next; - } - return vd; -} - -esp_err_t esp_intr_mark_shared(int intno, int cpu, bool is_int_ram) -{ - if (intno>31) return ESP_ERR_INVALID_ARG; - if (cpu>=portNUM_PROCESSORS) return ESP_ERR_INVALID_ARG; - - portENTER_CRITICAL(&spinlock); - vector_desc_t *vd=get_desc_for_int(intno, cpu); - if (vd==NULL) { - portEXIT_CRITICAL(&spinlock); - return ESP_ERR_NO_MEM; - } - vd->flags=VECDESC_FL_SHARED; - if (is_int_ram) vd->flags|=VECDESC_FL_INIRAM; - portEXIT_CRITICAL(&spinlock); - - return ESP_OK; -} - -esp_err_t esp_intr_reserve(int intno, int cpu) -{ - if (intno>31) return ESP_ERR_INVALID_ARG; - if (cpu>=portNUM_PROCESSORS) return ESP_ERR_INVALID_ARG; - - portENTER_CRITICAL(&spinlock); - vector_desc_t *vd=get_desc_for_int(intno, cpu); - if (vd==NULL) { - portEXIT_CRITICAL(&spinlock); - return ESP_ERR_NO_MEM; - } - vd->flags=VECDESC_FL_RESERVED; - portEXIT_CRITICAL(&spinlock); - - return ESP_OK; -} - -//Interrupt handler table and unhandled uinterrupt routine. Duplicated -//from xtensa_intr.c... it's supposed to be private, but we need to look -//into it in order to see if someone allocated an int using -//xt_set_interrupt_handler. -typedef struct xt_handler_table_entry { - void * handler; - void * arg; -} xt_handler_table_entry; -extern xt_handler_table_entry _xt_interrupt_table[XCHAL_NUM_INTERRUPTS*portNUM_PROCESSORS]; -extern void xt_unhandled_interrupt(void * arg); - -//Returns true if handler for interrupt is not the default unhandled interrupt handler -static bool int_has_handler(int intr, int cpu) -{ - return (_xt_interrupt_table[intr*portNUM_PROCESSORS+cpu].handler != xt_unhandled_interrupt); -} - -static bool is_vect_desc_usable(vector_desc_t *vd, int flags, int cpu, int force) -{ - //Check if interrupt is not reserved by design - int x = vd->intno; - if (int_desc[x].cpuflags[cpu]==INTDESC_RESVD) { - ALCHLOG("....Unusable: reserved"); - return false; - } - if (int_desc[x].cpuflags[cpu]==INTDESC_SPECIAL && force==-1) { - ALCHLOG("....Unusable: special-purpose int"); - return false; - } - //Check if the interrupt level is acceptable - if (!(flags&(1<flags&VECDESC_FL_RESERVED) { - ALCHLOG("....Unusable: reserved at runtime."); - return false; - } - //Ints can't be both shared and non-shared. - assert(!((vd->flags&VECDESC_FL_SHARED)&&(vd->flags&VECDESC_FL_NONSHARED))); - //check if interrupt already is in use by a non-shared interrupt - if (vd->flags&VECDESC_FL_NONSHARED) { - ALCHLOG("....Unusable: already in (non-shared) use."); - return false; - } - // check shared interrupt flags - if (vd->flags&VECDESC_FL_SHARED ) { - if (flags&ESP_INTR_FLAG_SHARED) { - bool in_iram_flag=((flags&ESP_INTR_FLAG_IRAM)!=0); - bool desc_in_iram_flag=((vd->flags&VECDESC_FL_INIRAM)!=0); - //Bail out if int is shared, but iram property doesn't match what we want. - if ((vd->flags&VECDESC_FL_SHARED) && (desc_in_iram_flag!=in_iram_flag)) { - ALCHLOG("....Unusable: shared but iram prop doesn't match"); - return false; - } - } else { - //We need an unshared IRQ; can't use shared ones; bail out if this is shared. - ALCHLOG("...Unusable: int is shared, we need non-shared."); - return false; - } - } else if (int_has_handler(x, cpu)) { - //Check if interrupt already is allocated by xt_set_interrupt_handler - ALCHLOG("....Unusable: already allocated"); - return false; - } - return true; -} - -//Locate a free interrupt compatible with the flags given. -//The 'force' argument can be -1, or 0-31 to force checking a certain interrupt. -//When a CPU is forced, the INTDESC_SPECIAL marked interrupts are also accepted. -static int get_available_int(int flags, int cpu, int force, int source) -{ - int x; - int best=-1; - int bestLevel=9; - int bestSharedCt=INT_MAX; - //Default vector desc, for vectors not in the linked list - vector_desc_t empty_vect_desc; - memset(&empty_vect_desc, 0, sizeof(vector_desc_t)); - - //Level defaults to any low/med interrupt - if (!(flags&ESP_INTR_FLAG_LEVELMASK)) flags|=ESP_INTR_FLAG_LOWMED; - - ALCHLOG("get_available_int: try to find existing. Cpu: %d, Source: %d", cpu, source); - vector_desc_t *vd = find_desc_for_source(source, cpu); - if ( vd ) { - // if existing vd found, don't need to search any more. - ALCHLOG("get_avalible_int: existing vd found. intno: %d", vd->intno); - if ( force != -1 && force != vd->intno ) { - ALCHLOG("get_avalible_int: intr forced but not matach existing. existing intno: %d, force: %d", vd->intno, force); - } else if ( !is_vect_desc_usable(vd, flags, cpu, force) ) { - ALCHLOG("get_avalible_int: existing vd invalid."); - } else { - best = vd->intno; - } - return best; - } - if (force!=-1) { - ALCHLOG("get_available_int: try to find force. Cpu: %d, Source: %d, Force: %d", cpu, source, force); - //if force assigned, don't need to search any more. - vd = find_desc_for_int(force, cpu); - if (vd == NULL ) { - //if existing vd not found, just check the default state for the intr. - empty_vect_desc.intno = force; - vd = &empty_vect_desc; - } - if ( is_vect_desc_usable(vd, flags, cpu, force) ) { - best = vd->intno; - } else { - ALCHLOG("get_avalible_int: forced vd invalid."); - } - return best; - } - - ALCHLOG("get_free_int: start looking. Current cpu: %d", cpu); - //No allocated handlers as well as forced intr, iterate over the 32 possible interrupts - for (x=0; x<32; x++) { - //Grab the vector_desc for this vector. - vd=find_desc_for_int(x, cpu); - if (vd==NULL) { - empty_vect_desc.intno = x; - vd=&empty_vect_desc; - } - - ALCHLOG("Int %d reserved %d level %d %s hasIsr %d", - x, int_desc[x].cpuflags[cpu]==INTDESC_RESVD, int_desc[x].level, - int_desc[x].type==INTTP_LEVEL?"LEVEL":"EDGE", int_has_handler(x, cpu)); - if ( !is_vect_desc_usable(vd, flags, cpu, force) ) continue; - - if (flags&ESP_INTR_FLAG_SHARED) { - //We're allocating a shared int. - //See if int already is used as a shared interrupt. - if (vd->flags&VECDESC_FL_SHARED) { - //We can use this already-marked-as-shared interrupt. Count the already attached isrs in order to see - //how useful it is. - int no=0; - shared_vector_desc_t *svdesc=vd->shared_vec_info; - while (svdesc!=NULL) { - no++; - svdesc=svdesc->next; - } - if (noint_desc[x].level) { - //Seems like this shared vector is both okay and has the least amount of ISRs already attached to it. - best=x; - bestSharedCt=no; - bestLevel=int_desc[x].level; - ALCHLOG("...int %d more usable as a shared int: has %d existing vectors", x, no); - } else { - ALCHLOG("...worse than int %d", best); - } - } else { - if (best==-1) { - //We haven't found a feasible shared interrupt yet. This one is still free and usable, even if - //not marked as shared. - //Remember it in case we don't find any other shared interrupt that qualifies. - if (bestLevel>int_desc[x].level) { - best=x; - bestLevel=int_desc[x].level; - ALCHLOG("...int %d usable as a new shared int", x); - } - } else { - ALCHLOG("...already have a shared int"); - } - } - } else { - //Seems this interrupt is feasible. Select it and break out of the loop; no need to search further. - if (bestLevel>int_desc[x].level) { - best=x; - bestLevel=int_desc[x].level; - } else { - ALCHLOG("...worse than int %d", best); - } - } - } - ALCHLOG("get_available_int: using int %d", best); - - //Okay, by now we have looked at all potential interrupts and hopefully have selected the best one in best. - return best; -} - -//Common shared isr handler. Chain-call all ISRs. -static void IRAM_ATTR shared_intr_isr(void *arg) -{ - vector_desc_t *vd=(vector_desc_t*)arg; - shared_vector_desc_t *sh_vec=vd->shared_vec_info; - portENTER_CRITICAL_ISR(&spinlock); - while(sh_vec) { - if (!sh_vec->disabled) { - if ((sh_vec->statusreg == NULL) || (*sh_vec->statusreg & sh_vec->statusmask)) { -#if CONFIG_SYSVIEW_ENABLE - traceISR_ENTER(sh_vec->source+ETS_INTERNAL_INTR_SOURCE_OFF); -#endif - sh_vec->isr(sh_vec->arg); -#if CONFIG_SYSVIEW_ENABLE - // check if we will return to scheduler or to interrupted task after ISR - if (!port_switch_flag[xPortGetCoreID()]) { - traceISR_EXIT(); - } -#endif - } - } - sh_vec=sh_vec->next; - } - portEXIT_CRITICAL_ISR(&spinlock); -} - -#if CONFIG_SYSVIEW_ENABLE -//Common non-shared isr handler wrapper. -static void IRAM_ATTR non_shared_intr_isr(void *arg) -{ - non_shared_isr_arg_t *ns_isr_arg=(non_shared_isr_arg_t*)arg; - portENTER_CRITICAL_ISR(&spinlock); - traceISR_ENTER(ns_isr_arg->source+ETS_INTERNAL_INTR_SOURCE_OFF); - // FIXME: can we call ISR and check port_switch_flag after releasing spinlock? - // when CONFIG_SYSVIEW_ENABLE = 0 ISRs for non-shared IRQs are called without spinlock - ns_isr_arg->isr(ns_isr_arg->isr_arg); - // check if we will return to scheduler or to interrupted task after ISR - if (!port_switch_flag[xPortGetCoreID()]) { - traceISR_EXIT(); - } - portEXIT_CRITICAL_ISR(&spinlock); -} -#endif - -//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) -{ - intr_handle_data_t *ret=NULL; - int force=-1; - ESP_EARLY_LOGV(TAG, "esp_intr_alloc_intrstatus (cpu %d): checking args", xPortGetCoreID()); - //Shared interrupts should be level-triggered. - if ((flags&ESP_INTR_FLAG_SHARED) && (flags&ESP_INTR_FLAG_EDGE)) return ESP_ERR_INVALID_ARG; - //You can't set an handler / arg for a non-C-callable interrupt. - if ((flags&ESP_INTR_FLAG_HIGH) && (handler)) return ESP_ERR_INVALID_ARG; - //Shared ints should have handler and non-processor-local source - if ((flags&ESP_INTR_FLAG_SHARED) && (!handler || source<0)) return ESP_ERR_INVALID_ARG; - //Statusreg should have a mask - if (intrstatusreg && !intrstatusmask) return ESP_ERR_INVALID_ARG; - //If the ISR is marked to be IRAM-resident, the handler must not be in the cached region - if ((flags&ESP_INTR_FLAG_IRAM) && - (ptrdiff_t) handler >= SOC_RTC_IRAM_HIGH && - (ptrdiff_t) handler < SOC_RTC_DATA_LOW ) { - 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) { - flags|=ESP_INTR_FLAG_LEVEL1; - } else { - flags|=ESP_INTR_FLAG_LOWMED; - } - } - ESP_EARLY_LOGV(TAG, "esp_intr_alloc_intrstatus (cpu %d): Args okay. Resulting flags 0x%X", xPortGetCoreID(), flags); - - //Check 'special' interrupt sources. These are tied to one specific interrupt, so we - //have to force get_free_int to only look at that. - if (source==ETS_INTERNAL_TIMER0_INTR_SOURCE) force=ETS_INTERNAL_TIMER0_INTR_NO; - if (source==ETS_INTERNAL_TIMER1_INTR_SOURCE) force=ETS_INTERNAL_TIMER1_INTR_NO; - if (source==ETS_INTERNAL_TIMER2_INTR_SOURCE) force=ETS_INTERNAL_TIMER2_INTR_NO; - if (source==ETS_INTERNAL_SW0_INTR_SOURCE) force=ETS_INTERNAL_SW0_INTR_NO; - if (source==ETS_INTERNAL_SW1_INTR_SOURCE) force=ETS_INTERNAL_SW1_INTR_NO; - if (source==ETS_INTERNAL_PROFILING_INTR_SOURCE) force=ETS_INTERNAL_PROFILING_INTR_NO; - - //Allocate a return handle. If we end up not needing it, we'll free it later on. - ret=heap_caps_malloc(sizeof(intr_handle_data_t), MALLOC_CAP_INTERNAL|MALLOC_CAP_8BIT); - if (ret==NULL) return ESP_ERR_NO_MEM; - - portENTER_CRITICAL(&spinlock); - int cpu=xPortGetCoreID(); - //See if we can find an interrupt that matches the flags. - int intr=get_available_int(flags, cpu, force, source); - if (intr==-1) { - //None found. Bail out. - portEXIT_CRITICAL(&spinlock); - free(ret); - return ESP_ERR_NOT_FOUND; - } - //Get an int vector desc for int. - vector_desc_t *vd=get_desc_for_int(intr, cpu); - if (vd==NULL) { - portEXIT_CRITICAL(&spinlock); - free(ret); - return ESP_ERR_NO_MEM; - } - - //Allocate that int! - if (flags&ESP_INTR_FLAG_SHARED) { - //Populate vector entry and add to linked list. - shared_vector_desc_t *sh_vec=malloc(sizeof(shared_vector_desc_t)); - if (sh_vec==NULL) { - portEXIT_CRITICAL(&spinlock); - free(ret); - return ESP_ERR_NO_MEM; - } - memset(sh_vec, 0, sizeof(shared_vector_desc_t)); - sh_vec->statusreg=(uint32_t*)intrstatusreg; - sh_vec->statusmask=intrstatusmask; - sh_vec->isr=handler; - sh_vec->arg=arg; - sh_vec->next=vd->shared_vec_info; - sh_vec->source=source; - sh_vec->disabled=0; - vd->shared_vec_info=sh_vec; - vd->flags|=VECDESC_FL_SHARED; - //(Re-)set shared isr handler to new value. - xt_set_interrupt_handler(intr, shared_intr_isr, vd); - } else { - //Mark as unusable for other interrupt sources. This is ours now! - vd->flags=VECDESC_FL_NONSHARED; - if (handler) { -#if CONFIG_SYSVIEW_ENABLE - non_shared_isr_arg_t *ns_isr_arg=malloc(sizeof(non_shared_isr_arg_t)); - if (!ns_isr_arg) { - portEXIT_CRITICAL(&spinlock); - free(ret); - return ESP_ERR_NO_MEM; - } - ns_isr_arg->isr=handler; - ns_isr_arg->isr_arg=arg; - ns_isr_arg->source=source; - xt_set_interrupt_handler(intr, non_shared_intr_isr, ns_isr_arg); -#else - xt_set_interrupt_handler(intr, handler, arg); -#endif - } - if (flags&ESP_INTR_FLAG_EDGE) xthal_set_intclear(1 << intr); - vd->source=source; - } - if (flags&ESP_INTR_FLAG_IRAM) { - vd->flags|=VECDESC_FL_INIRAM; - non_iram_int_mask[cpu]&=~(1<flags&=~VECDESC_FL_INIRAM; - non_iram_int_mask[cpu]|=(1<=0) { - intr_matrix_set(cpu, source, intr); - } - - //Fill return handle data. - ret->vector_desc=vd; - ret->shared_vector_desc=vd->shared_vec_info; - - //Enable int at CPU-level; - ESP_INTR_ENABLE(intr); - - //If interrupt has to be started disabled, do that now; ints won't be enabled for real until the end - //of the critical section. - if (flags&ESP_INTR_FLAG_INTRDISABLED) { - esp_intr_disable(ret); - } - - portEXIT_CRITICAL(&spinlock); - - //Fill return handle if needed, otherwise free handle. - if (ret_handle!=NULL) { - *ret_handle=ret; - } else { - free(ret); - } - - ESP_EARLY_LOGD(TAG, "Connected src %d to int %d (cpu %d)", source, intr, cpu); - return ESP_OK; -} - -esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *arg, intr_handle_t *ret_handle) -{ - /* - As an optimization, we can create a table with the possible interrupt status registers and masks for every single - source there is. We can then add code here to look up an applicable value and pass that to the - esp_intr_alloc_intrstatus function. - */ - return esp_intr_alloc_intrstatus(source, flags, 0, 0, handler, arg, ret_handle); -} - -esp_err_t IRAM_ATTR esp_intr_set_in_iram(intr_handle_t handle, bool is_in_iram) -{ - if (!handle) return ESP_ERR_INVALID_ARG; - vector_desc_t *vd = handle->vector_desc; - if (vd->flags & VECDESC_FL_SHARED) { - return ESP_ERR_INVALID_ARG; - } - portENTER_CRITICAL(&spinlock); - uint32_t mask = (1 << vd->intno); - if (is_in_iram) { - vd->flags |= VECDESC_FL_INIRAM; - non_iram_int_mask[vd->cpu] &= ~mask; - } else { - vd->flags &= ~VECDESC_FL_INIRAM; - non_iram_int_mask[vd->cpu] |= mask; - } - portEXIT_CRITICAL(&spinlock); - return ESP_OK; -} - -esp_err_t esp_intr_free(intr_handle_t handle) -{ - bool free_shared_vector=false; - if (!handle) return ESP_ERR_INVALID_ARG; - //This routine should be called from the interrupt the task is scheduled on. - if (handle->vector_desc->cpu!=xPortGetCoreID()) return ESP_ERR_INVALID_ARG; - - portENTER_CRITICAL(&spinlock); - esp_intr_disable(handle); - if (handle->vector_desc->flags&VECDESC_FL_SHARED) { - //Find and kill the shared int - shared_vector_desc_t *svd=handle->vector_desc->shared_vec_info; - shared_vector_desc_t *prevsvd=NULL; - assert(svd); //should be something in there for a shared int - while (svd!=NULL) { - if (svd==handle->shared_vector_desc) { - //Found it. Now kill it. - if (prevsvd) { - prevsvd->next=svd->next; - } else { - handle->vector_desc->shared_vec_info=svd->next; - } - free(svd); - break; - } - prevsvd=svd; - svd=svd->next; - } - //If nothing left, disable interrupt. - if (handle->vector_desc->shared_vec_info==NULL) free_shared_vector=true; - ESP_EARLY_LOGV(TAG, "esp_intr_free: Deleting shared int: %s. Shared int is %s", svd?"not found or last one":"deleted", free_shared_vector?"empty now.":"still in use"); - } - - if ((handle->vector_desc->flags&VECDESC_FL_NONSHARED) || free_shared_vector) { - ESP_EARLY_LOGV(TAG, "esp_intr_free: Disabling int, killing handler"); -#if CONFIG_SYSVIEW_ENABLE - if (!free_shared_vector) { - void *isr_arg = xt_get_interrupt_handler_arg(handle->vector_desc->intno); - if (isr_arg) { - free(isr_arg); - } - } -#endif - //Reset to normal handler - xt_set_interrupt_handler(handle->vector_desc->intno, xt_unhandled_interrupt, (void*)((int)handle->vector_desc->intno)); - //Theoretically, we could free the vector_desc... not sure if that's worth the few bytes of memory - //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); - //Also kill non_iram mask bit. - non_iram_int_mask[handle->vector_desc->cpu]&=~(1<<(handle->vector_desc->intno)); - } - portEXIT_CRITICAL(&spinlock); - free(handle); - return ESP_OK; -} - -int esp_intr_get_intno(intr_handle_t handle) -{ - return handle->vector_desc->intno; -} - -int esp_intr_get_cpu(intr_handle_t handle) -{ - return handle->vector_desc->cpu; -} - -/* - Interrupt disabling strategy: - If the source is >=0 (meaning a muxed interrupt), we disable it by muxing the interrupt to a non-connected - interrupt. If the source is <0 (meaning an internal, per-cpu interrupt), we disable it using ESP_INTR_DISABLE. - This allows us to, for the muxed CPUs, disable an int from the other core. It also allows disabling shared - interrupts. - */ - -//Muxing an interrupt source to interrupt 6, 7, 11, 15, 16 or 29 cause the interrupt to effectively be disabled. -#define INT_MUX_DISABLED_INTNO 6 - -esp_err_t IRAM_ATTR esp_intr_enable(intr_handle_t handle) -{ - if (!handle) return ESP_ERR_INVALID_ARG; - portENTER_CRITICAL_SAFE(&spinlock); - int source; - if (handle->shared_vector_desc) { - handle->shared_vector_desc->disabled=0; - source=handle->shared_vector_desc->source; - } else { - source=handle->vector_desc->source; - } - if (source >= 0) { - //Disabled using int matrix; re-connect to enable - intr_matrix_set(handle->vector_desc->cpu, source, handle->vector_desc->intno); - } else { - //Re-enable using cpu int ena reg - if (handle->vector_desc->cpu!=xPortGetCoreID()) return ESP_ERR_INVALID_ARG; //Can only enable these ints on this cpu - ESP_INTR_ENABLE(handle->vector_desc->intno); - } - portEXIT_CRITICAL_SAFE(&spinlock); - return ESP_OK; -} - -esp_err_t IRAM_ATTR esp_intr_disable(intr_handle_t handle) -{ - if (!handle) return ESP_ERR_INVALID_ARG; - portENTER_CRITICAL_SAFE(&spinlock); - int source; - bool disabled = 1; - if (handle->shared_vector_desc) { - handle->shared_vector_desc->disabled=1; - source=handle->shared_vector_desc->source; - - shared_vector_desc_t *svd=handle->vector_desc->shared_vec_info; - assert( svd != NULL ); - while( svd ) { - if ( svd->source == source && svd->disabled == 0 ) { - disabled = 0; - break; - } - svd = svd->next; - } - } else { - source=handle->vector_desc->source; - } - - if (source >= 0) { - if ( disabled ) { - //Disable using int matrix - intr_matrix_set(handle->vector_desc->cpu, source, INT_MUX_DISABLED_INTNO); - } - } else { - //Disable using per-cpu regs - if (handle->vector_desc->cpu!=xPortGetCoreID()) { - portEXIT_CRITICAL_SAFE(&spinlock); - return ESP_ERR_INVALID_ARG; //Can only enable these ints on this cpu - } - ESP_INTR_DISABLE(handle->vector_desc->intno); - } - portEXIT_CRITICAL_SAFE(&spinlock); - return ESP_OK; -} - - -void IRAM_ATTR esp_intr_noniram_disable(void) -{ - int oldint; - int cpu=xPortGetCoreID(); - int intmask=~non_iram_int_mask[cpu]; - if (non_iram_int_disabled_flag[cpu]) abort(); - non_iram_int_disabled_flag[cpu]=true; - asm volatile ( - "movi %0,0\n" - "xsr %0,INTENABLE\n" //disable all ints first - "rsync\n" - "and a3,%0,%1\n" //mask ints that need disabling - "wsr a3,INTENABLE\n" //write back - "rsync\n" - :"=&r"(oldint):"r"(intmask):"a3"); - //Save which ints we did disable - non_iram_int_disabled[cpu]=oldint&non_iram_int_mask[cpu]; -} - -void IRAM_ATTR esp_intr_noniram_enable(void) -{ - int cpu=xPortGetCoreID(); - int intmask=non_iram_int_disabled[cpu]; - if (!non_iram_int_disabled_flag[cpu]) abort(); - non_iram_int_disabled_flag[cpu]=false; - asm volatile ( - "movi a3,0\n" - "xsr a3,INTENABLE\n" - "rsync\n" - "or a3,a3,%0\n" - "wsr a3,INTENABLE\n" - "rsync\n" - ::"r"(intmask):"a3"); -} - -//These functions are provided in ROM, but the ROM-based functions use non-multicore-capable -//virtualized interrupt levels. Thus, we disable them in the ld file and provide working -//equivalents here. - - -void IRAM_ATTR ets_isr_unmask(unsigned int mask) { - xt_ints_on(mask); -} - -void IRAM_ATTR ets_isr_mask(unsigned int mask) { - xt_ints_off(mask); -} - - - - diff --git a/components/esp32s2/test/test_intr_alloc.c b/components/esp32s2/test/test_intr_alloc.c deleted file mode 100644 index e80fb4762e..0000000000 --- a/components/esp32s2/test/test_intr_alloc.c +++ /dev/null @@ -1,291 +0,0 @@ -/* - Tests for the interrupt allocator. -*/ - -#include -#include "unity.h" -#include "esp_types.h" -#include "esp_rom_sys.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "freertos/semphr.h" -#include "freertos/queue.h" -#include "freertos/xtensa_api.h" -#include "soc/uart_periph.h" -#include "soc/dport_reg.h" -#include "soc/gpio_periph.h" -#include "esp_intr_alloc.h" -#include "driver/periph_ctrl.h" -#include "driver/timer.h" - -#define TIMER_DIVIDER 16 /*!< Hardware timer clock divider */ -#define TIMER_SCALE (TIMER_BASE_CLK / TIMER_DIVIDER) /*!< used to calculate counter value */ -#define TIMER_INTERVAL0_SEC (3.4179) /*!< test interval for timer 0 */ -#define TIMER_INTERVAL1_SEC (5.78) /*!< test interval for timer 1 */ - -static void my_timer_init(int timer_group, int timer_idx, int ival) -{ - timer_config_t config; - config.alarm_en = 1; - config.auto_reload = 1; - config.counter_dir = TIMER_COUNT_UP; - config.divider = TIMER_DIVIDER; - config.intr_type = TIMER_INTR_LEVEL; - config.counter_en = TIMER_PAUSE; - /*Configure timer*/ - timer_init(timer_group, timer_idx, &config); - /*Stop timer counter*/ - timer_pause(timer_group, timer_idx); - /*Load counter value */ - timer_set_counter_value(timer_group, timer_idx, 0x00000000ULL); - /*Set alarm value*/ - timer_set_alarm_value(timer_group, timer_idx, ival); - /*Enable timer interrupt*/ - timer_enable_intr(timer_group, timer_idx); -} - -static volatile int count[4] = {0, 0, 0, 0}; - -static void timer_isr(void *arg) -{ - int timer_idx = (int)arg; - count[timer_idx]++; - if (timer_idx == 0) { - timer_group_clr_intr_status_in_isr(TIMER_GROUP_0, TIMER_0); - timer_group_enable_alarm_in_isr(TIMER_GROUP_0, TIMER_0); - } - if (timer_idx == 1) { - timer_group_clr_intr_status_in_isr(TIMER_GROUP_0, TIMER_1); - timer_group_enable_alarm_in_isr(TIMER_GROUP_0, TIMER_1); - } - if (timer_idx == 2) { - timer_group_clr_intr_status_in_isr(TIMER_GROUP_1, TIMER_0); - timer_group_enable_alarm_in_isr(TIMER_GROUP_1, TIMER_0); - } - if (timer_idx == 3) { - timer_group_clr_intr_status_in_isr(TIMER_GROUP_1, TIMER_1); - timer_group_enable_alarm_in_isr(TIMER_GROUP_1, TIMER_1); - } -} - -static void timer_test(int flags) -{ - int x; - timer_isr_handle_t inth[4]; - my_timer_init(TIMER_GROUP_0, TIMER_0, 110000); - my_timer_init(TIMER_GROUP_0, TIMER_1, 120000); - my_timer_init(TIMER_GROUP_1, TIMER_0, 130000); - my_timer_init(TIMER_GROUP_1, TIMER_1, 140000); - timer_isr_register(TIMER_GROUP_0, TIMER_0, timer_isr, (void *)0, flags | ESP_INTR_FLAG_INTRDISABLED, &inth[0]); - timer_isr_register(TIMER_GROUP_0, TIMER_1, timer_isr, (void *)1, flags, &inth[1]); - timer_isr_register(TIMER_GROUP_1, TIMER_0, timer_isr, (void *)2, flags, &inth[2]); - timer_isr_register(TIMER_GROUP_1, TIMER_1, timer_isr, (void *)3, flags, &inth[3]); - timer_start(TIMER_GROUP_0, TIMER_0); - timer_start(TIMER_GROUP_0, TIMER_1); - timer_start(TIMER_GROUP_1, TIMER_0); - timer_start(TIMER_GROUP_1, TIMER_1); - - for (x = 0; x < 4; x++) { - count[x] = 0; - } - printf("Interrupts allocated: %d (dis) %d %d %d\n", - esp_intr_get_intno(inth[0]), esp_intr_get_intno(inth[1]), - esp_intr_get_intno(inth[2]), esp_intr_get_intno(inth[3])); - printf("Timer values on start: %d %d %d %d\n", count[0], count[1], count[2], count[3]); - vTaskDelay(1000 / portTICK_PERIOD_MS); - printf("Timer values after 1 sec: %d %d %d %d\n", count[0], count[1], count[2], count[3]); - TEST_ASSERT(count[0] == 0); - TEST_ASSERT(count[1] != 0); - TEST_ASSERT(count[2] != 0); - TEST_ASSERT(count[3] != 0); - - printf("Disabling timers 1 and 2...\n"); - esp_intr_enable(inth[0]); - esp_intr_disable(inth[1]); - esp_intr_disable(inth[2]); - for (x = 0; x < 4; x++) { - count[x] = 0; - } - vTaskDelay(1000 / portTICK_PERIOD_MS); - printf("Timer values after 1 sec: %d %d %d %d\n", count[0], count[1], count[2], count[3]); - TEST_ASSERT(count[0] != 0); - TEST_ASSERT(count[1] == 0); - TEST_ASSERT(count[2] == 0); - TEST_ASSERT(count[3] != 0); - printf("Disabling other half...\n"); - esp_intr_enable(inth[1]); - esp_intr_enable(inth[2]); - esp_intr_disable(inth[0]); - esp_intr_disable(inth[3]); - for (x = 0; x < 4; x++) { - count[x] = 0; - } - vTaskDelay(1000 / portTICK_PERIOD_MS); - printf("Timer values after 1 sec: %d %d %d %d\n", count[0], count[1], count[2], count[3]); - TEST_ASSERT(count[0] == 0); - TEST_ASSERT(count[1] != 0); - TEST_ASSERT(count[2] != 0); - TEST_ASSERT(count[3] == 0); - printf("Done.\n"); - esp_intr_free(inth[0]); - esp_intr_free(inth[1]); - esp_intr_free(inth[2]); - esp_intr_free(inth[3]); -} - -static volatile int int_timer_ctr; - -void int_timer_handler(void *arg) -{ - xthal_set_ccompare(1, xthal_get_ccount() + 8000000); - int_timer_ctr++; -} - -void local_timer_test(void) -{ - intr_handle_t ih; - esp_err_t r; - r = esp_intr_alloc(ETS_INTERNAL_TIMER1_INTR_SOURCE, 0, int_timer_handler, NULL, &ih); - TEST_ASSERT(r == ESP_OK); - printf("Int timer 1 intno %d\n", esp_intr_get_intno(ih)); - xthal_set_ccompare(1, xthal_get_ccount() + 8000000); - int_timer_ctr = 0; - vTaskDelay(1000 / portTICK_PERIOD_MS); - printf("Timer val after 1 sec: %d\n", int_timer_ctr); - TEST_ASSERT(int_timer_ctr != 0); - printf("Disabling int\n"); - esp_intr_disable(ih); - int_timer_ctr = 0; - vTaskDelay(1000 / portTICK_PERIOD_MS); - printf("Timer val after 1 sec: %d\n", int_timer_ctr); - TEST_ASSERT(int_timer_ctr == 0); - printf("Re-enabling\n"); - esp_intr_enable(ih); - vTaskDelay(1000 / portTICK_PERIOD_MS); - printf("Timer val after 1 sec: %d\n", int_timer_ctr); - TEST_ASSERT(int_timer_ctr != 0); - - printf("Free int, re-alloc disabled\n"); - r = esp_intr_free(ih); - TEST_ASSERT(r == ESP_OK); - r = esp_intr_alloc(ETS_INTERNAL_TIMER1_INTR_SOURCE, ESP_INTR_FLAG_INTRDISABLED, int_timer_handler, NULL, &ih); - TEST_ASSERT(r == ESP_OK); - int_timer_ctr = 0; - vTaskDelay(1000 / portTICK_PERIOD_MS); - printf("Timer val after 1 sec: %d\n", int_timer_ctr); - TEST_ASSERT(int_timer_ctr == 0); - printf("Re-enabling\n"); - esp_intr_enable(ih); - vTaskDelay(1000 / portTICK_PERIOD_MS); - printf("Timer val after 1 sec: %d\n", int_timer_ctr); - TEST_ASSERT(int_timer_ctr != 0); - r = esp_intr_free(ih); - TEST_ASSERT(r == ESP_OK); - printf("Done.\n"); -} - -TEST_CASE("Intr_alloc test, CPU-local int source", "[intr_alloc]") -{ - local_timer_test(); -} - -TEST_CASE("Intr_alloc test, private ints", "[intr_alloc]") -{ - timer_test(0); -} - -TEST_CASE("Intr_alloc test, shared ints", "[intr_alloc]") -{ - timer_test(ESP_INTR_FLAG_SHARED); -} - -TEST_CASE("Can allocate IRAM int only with an IRAM handler", "[intr_alloc]") -{ - void dummy(void *arg) { - } - IRAM_ATTR void dummy_iram(void *arg) { - } - RTC_IRAM_ATTR void dummy_rtc(void *arg) { - } - intr_handle_t ih; - esp_err_t err = esp_intr_alloc(ETS_INTERNAL_SW0_INTR_SOURCE, - ESP_INTR_FLAG_IRAM, &dummy, NULL, &ih); - TEST_ASSERT_EQUAL_INT(ESP_ERR_INVALID_ARG, err); - err = esp_intr_alloc(ETS_INTERNAL_SW0_INTR_SOURCE, - ESP_INTR_FLAG_IRAM, &dummy_iram, NULL, &ih); - TEST_ESP_OK(err); - err = esp_intr_free(ih); - TEST_ESP_OK(err); - err = esp_intr_alloc(ETS_INTERNAL_SW0_INTR_SOURCE, - ESP_INTR_FLAG_IRAM, &dummy_rtc, NULL, &ih); - TEST_ESP_OK(err); - err = esp_intr_free(ih); - TEST_ESP_OK(err); -} - -#include "soc/spi_periph.h" -typedef struct { - bool flag1; - bool flag2; - bool flag3; - bool flag4; -} intr_alloc_test_ctx_t; - -void IRAM_ATTR int_handler1(void *arg) -{ - intr_alloc_test_ctx_t *ctx = (intr_alloc_test_ctx_t *)arg; - esp_rom_printf("handler 1 called.\n"); - if (ctx->flag1) { - ctx->flag3 = true; - } else { - ctx->flag1 = true; - } - GPSPI2.slave.trans_done = 0; -} - -void IRAM_ATTR int_handler2(void *arg) -{ - intr_alloc_test_ctx_t *ctx = (intr_alloc_test_ctx_t *)arg; - esp_rom_printf("handler 2 called.\n"); - if (ctx->flag2) { - ctx->flag4 = true; - } else { - ctx->flag2 = true; - } -} - -TEST_CASE("allocate 2 handlers for a same source and remove the later one", "[intr_alloc]") -{ - intr_alloc_test_ctx_t ctx = {false, false, false, false}; - intr_handle_t handle1, handle2; - - //enable SPI2 - periph_module_enable(PERIPH_FSPI_MODULE); - - esp_err_t r; - r = esp_intr_alloc(ETS_SPI2_INTR_SOURCE, ESP_INTR_FLAG_SHARED, int_handler1, &ctx, &handle1); - TEST_ESP_OK(r); - //try an invalid assign first - r = esp_intr_alloc(ETS_SPI2_INTR_SOURCE, 0, int_handler2, NULL, &handle2); - TEST_ASSERT_EQUAL_INT(r, ESP_ERR_NOT_FOUND); - //assign shared then - r = esp_intr_alloc(ETS_SPI2_INTR_SOURCE, ESP_INTR_FLAG_SHARED, int_handler2, &ctx, &handle2); - TEST_ESP_OK(r); - GPSPI2.slave.int_trans_done_en = 1; - - printf("trigger first time.\n"); - GPSPI2.slave.trans_done = 1; - - vTaskDelay(100); - TEST_ASSERT(ctx.flag1 && ctx.flag2); - - printf("remove intr 1.\n"); - r = esp_intr_free(handle2); - - printf("trigger second time.\n"); - GPSPI2.slave.trans_done = 1; - - vTaskDelay(500); - TEST_ASSERT(ctx.flag3 && !ctx.flag4); - printf("test passed.\n"); -} diff --git a/components/esp32s3/CMakeLists.txt b/components/esp32s3/CMakeLists.txt index 2d3dc606b4..1d060d9a4b 100644 --- a/components/esp32s3/CMakeLists.txt +++ b/components/esp32s3/CMakeLists.txt @@ -17,7 +17,7 @@ else() "crosscore_int.c" "dport_access.c" "hw_random.c" - "intr_alloc.c" + "memprot.c" "spiram.c" "spiram_psram.c" diff --git a/components/esp32s3/include/esp_intr_alloc.h b/components/esp32s3/include/esp_intr_alloc.h deleted file mode 100644 index 9b3bf662bc..0000000000 --- a/components/esp32s3/include/esp_intr_alloc.h +++ /dev/null @@ -1,299 +0,0 @@ -// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef __ESP_INTR_ALLOC_H__ -#define __ESP_INTR_ALLOC_H__ - -#include -#include -#include "esp_err.h" -#include "freertos/xtensa_api.h" - -#ifdef __cplusplus -extern "C" { -#endif - - -/** @addtogroup Intr_Alloc - * @{ - */ - - -/** @brief Interrupt allocation flags - * - * These flags can be used to specify which interrupt qualities the - * code calling esp_intr_alloc* needs. - * - */ - -//Keep the LEVELx values as they are here; they match up with (1<3 - * is requested, because these types of interrupts aren't C-callable. - * @param arg Optional argument for passed to the interrupt handler - * @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 flags - * ESP_OK otherwise - */ -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. - * - * - * This essentially does the same as esp_intr_alloc, but allows specifying a register and mask - * combo. For shared interrupts, the handler is only called if a read from the specified - * register, ANDed with the mask, returns non-zero. By passing an interrupt status register - * address and a fitting mask, this can be used to accelerate interrupt handling in the case - * a shared interrupt is triggered; by checking the interrupt statuses first, the code can - * decide which ISRs can be skipped - * - * @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 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. - * @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 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 flags - * ESP_OK otherwise - */ -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 Disable and free an interrupt. - * - * Use an interrupt handle to disable the interrupt and release the resources - * associated with it. - * - * @note - * When the handler shares its source with other handlers, the interrupt status - * bits it's responsible for should be managed properly before freeing it. see - * ``esp_intr_disable`` for more details. - * - * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus - * - * @return ESP_ERR_INVALID_ARG if handle is invalid, or esp_intr_free runs on another core than - * where the interrupt is allocated on. - * ESP_OK otherwise - */ -esp_err_t esp_intr_free(intr_handle_t handle); - - -/** - * @brief Get CPU number an interrupt is tied to - * - * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus - * - * @return The core number where the interrupt is allocated - */ -int esp_intr_get_cpu(intr_handle_t handle); - -/** - * @brief Get the allocated interrupt for a certain handle - * - * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus - * - * @return The interrupt number - */ -int esp_intr_get_intno(intr_handle_t handle); - -/** - * @brief Disable the interrupt associated with the handle - * - * @note - * 1. For local interrupts (ESP_INTERNAL_* sources), this function has to be called on the - * CPU the interrupt is allocated on. Other interrupts have no such restriction. - * 2. When several handlers sharing a same interrupt source, interrupt status bits, which are - * handled in the handler to be disabled, should be masked before the disabling, or handled - * in other enabled interrupts properly. Miss of interrupt status handling will cause infinite - * interrupt calls and finally system crash. - * - * @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_err_t esp_intr_disable(intr_handle_t handle); - -/** - * @brief Enable the interrupt associated with the handle - * - * @note For local interrupts (ESP_INTERNAL_* sources), this function has to be called on the - * CPU the interrupt is allocated on. Other interrupts have no such restriction. - * - * @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_err_t esp_intr_enable(intr_handle_t handle); - -/** - * @brief Set the "in IRAM" status of the handler. - * - * @note Does not work on shared interrupts. - * - * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus - * @param is_in_iram Whether the handler associated with this handle resides in IRAM. - * 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_err_t esp_intr_set_in_iram(intr_handle_t handle, bool is_in_iram); - -/** - * @brief Disable interrupts that aren't specifically marked as running from IRAM - */ -void esp_intr_noniram_disable(void); - - -/** - * @brief Re-enable interrupts disabled by esp_intr_noniram_disable - */ -void esp_intr_noniram_enable(void); - -/**@}*/ - - -#ifdef __cplusplus -} -#endif - -#endif diff --git a/components/esp32s3/intr_alloc.c b/components/esp32s3/intr_alloc.c deleted file mode 100644 index c61d762608..0000000000 --- a/components/esp32s3/intr_alloc.c +++ /dev/null @@ -1,954 +0,0 @@ -// Copyright 2015-2020 Espressif Systems (Shanghai) PTE LTD -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#include -#include -#include -#include -#include -#include "sdkconfig.h" -#include "freertos/FreeRTOS.h" -#include "freertos/task.h" -#include "esp_err.h" -#include "esp_log.h" -#include "esp_intr_alloc.h" -#include "esp_attr.h" -#include "soc/soc.h" - -static const char *TAG = "intr_alloc"; - -#define ETS_INTERNAL_TIMER0_INTR_NO 6 -#define ETS_INTERNAL_TIMER1_INTR_NO 15 -#define ETS_INTERNAL_TIMER2_INTR_NO 16 -#define ETS_INTERNAL_SW0_INTR_NO 7 -#define ETS_INTERNAL_SW1_INTR_NO 29 -#define ETS_INTERNAL_PROFILING_INTR_NO 11 - -/* -Define this to debug the choices made when allocating the interrupt. This leads to much debugging -output within a critical region, which can lead to weird effects like e.g. the interrupt watchdog -being triggered, that is why it is separate from the normal LOG* scheme. -*/ -//define DEBUG_INT_ALLOC_DECISIONS -#ifdef DEBUG_INT_ALLOC_DECISIONS -# define ALCHLOG(...) ESP_EARLY_LOGD(TAG, __VA_ARGS__) -#else -# define ALCHLOG(...) do {} while (0) -#endif - - -typedef enum { - INTDESC_NORMAL = 0, - INTDESC_RESVD, - INTDESC_SPECIAL //for xtensa timers / software ints -} int_desc_flag_t; - -typedef enum { - INTTP_LEVEL = 0, - INTTP_EDGE, - INTTP_NA -} int_type_t; - -typedef struct { - int level; - int_type_t type; - int_desc_flag_t cpuflags[2]; -} int_desc_t; - - -//We should mark the interrupt for the timer used by FreeRTOS as reserved. The specific timer -//is selectable using menuconfig; we use these cpp bits to convert that into something we can use in -//the table below. -#if CONFIG_FREERTOS_CORETIMER_0 -#define INT6RES INTDESC_RESVD -#else -#define INT6RES INTDESC_SPECIAL -#endif - -#if CONFIG_FREERTOS_CORETIMER_1 -#define INT15RES INTDESC_RESVD -#else -#define INT15RES INTDESC_SPECIAL -#endif - -//This is basically a software-readable version of the interrupt usage table in include/soc/soc.h -const static int_desc_t int_desc[32] = { - { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //0 - { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //1 - { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //2 - { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //3 - { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_NORMAL} }, //4 - { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //5 - { 1, INTTP_NA, {INT6RES, INT6RES } }, //6 - { 1, INTTP_NA, {INTDESC_SPECIAL, INTDESC_SPECIAL}}, //7 - { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //8 - { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //9 - { 1, INTTP_EDGE, {INTDESC_NORMAL, INTDESC_NORMAL} }, //10 - { 3, INTTP_NA, {INTDESC_SPECIAL, INTDESC_SPECIAL}}, //11 - { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //12 - { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //13 - { 7, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //14, NMI - { 3, INTTP_NA, {INT15RES, INT15RES } }, //15 - { 5, INTTP_NA, {INTDESC_SPECIAL, INTDESC_SPECIAL} }, //16 - { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //17 - { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //18 - { 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //19 - { 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //20 - { 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //21 - { 3, INTTP_EDGE, {INTDESC_RESVD, INTDESC_NORMAL} }, //22 - { 3, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //23 - { 4, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_NORMAL} }, //24 - { 4, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //25 - { 5, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //26 - { 3, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //27 - { 4, INTTP_EDGE, {INTDESC_NORMAL, INTDESC_NORMAL} }, //28 - { 3, INTTP_NA, {INTDESC_SPECIAL, INTDESC_SPECIAL}}, //29 - { 4, INTTP_EDGE, {INTDESC_RESVD, INTDESC_RESVD } }, //30 - { 5, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //31 -}; - -typedef struct shared_vector_desc_t shared_vector_desc_t; -typedef struct vector_desc_t vector_desc_t; - -struct shared_vector_desc_t { - int disabled: 1; - int source: 8; - volatile uint32_t *statusreg; - uint32_t statusmask; - intr_handler_t isr; - void *arg; - shared_vector_desc_t *next; -}; - - -#define VECDESC_FL_RESERVED (1<<0) -#define VECDESC_FL_INIRAM (1<<1) -#define VECDESC_FL_SHARED (1<<2) -#define VECDESC_FL_NONSHARED (1<<3) - -//Pack using bitfields for better memory use -struct vector_desc_t { - int flags: 16; //OR of VECDESC_FLAG_* defines - unsigned int cpu: 1; - unsigned int intno: 5; - int source: 8; //Interrupt mux flags, used when not shared - shared_vector_desc_t *shared_vec_info; //used when VECDESC_FL_SHARED - vector_desc_t *next; -}; - -struct intr_handle_data_t { - vector_desc_t *vector_desc; - shared_vector_desc_t *shared_vector_desc; -}; - -typedef struct non_shared_isr_arg_t non_shared_isr_arg_t; - -struct non_shared_isr_arg_t { - intr_handler_t isr; - void *isr_arg; - int source; -}; - -//Linked list of vector descriptions, sorted by cpu.intno value -static vector_desc_t *vector_desc_head = NULL; - -//This bitmask has an 1 if the int should be disabled when the flash is disabled. -static uint32_t non_iram_int_mask[portNUM_PROCESSORS]; -//This bitmask has 1 in it if the int was disabled using esp_intr_noniram_disable. -static uint32_t non_iram_int_disabled[portNUM_PROCESSORS]; -static bool non_iram_int_disabled_flag[portNUM_PROCESSORS]; - -#if CONFIG_SYSVIEW_ENABLE -extern uint32_t port_switch_flag[]; -#endif - -static portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED; - -//Inserts an item into vector_desc list so that the list is sorted -//with an incrementing cpu.intno value. -static void insert_vector_desc(vector_desc_t *to_insert) -{ - vector_desc_t *vd = vector_desc_head; - vector_desc_t *prev = NULL; - while (vd != NULL) { - if (vd->cpu > to_insert->cpu) { - break; - } - if (vd->cpu == to_insert->cpu && vd->intno >= to_insert->intno) { - break; - } - prev = vd; - vd = vd->next; - } - if ((vector_desc_head == NULL) || (prev == NULL)) { - //First item - to_insert->next = vd; - vector_desc_head = to_insert; - } else { - prev->next = to_insert; - to_insert->next = vd; - } -} - -//Returns a vector_desc entry for an intno/cpu, or NULL if none exists. -static vector_desc_t *find_desc_for_int(int intno, int cpu) -{ - vector_desc_t *vd = vector_desc_head; - while (vd != NULL) { - if (vd->cpu == cpu && vd->intno == intno) { - break; - } - vd = vd->next; - } - return vd; -} - -//Returns a vector_desc entry for an intno/cpu. -//Either returns a preexisting one or allocates a new one and inserts -//it into the list. Returns NULL on malloc fail. -static vector_desc_t *get_desc_for_int(int intno, int cpu) -{ - vector_desc_t *vd = find_desc_for_int(intno, cpu); - if (vd == NULL) { - vector_desc_t *newvd = heap_caps_malloc(sizeof(vector_desc_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); - if (newvd == NULL) { - return NULL; - } - memset(newvd, 0, sizeof(vector_desc_t)); - newvd->intno = intno; - newvd->cpu = cpu; - insert_vector_desc(newvd); - return newvd; - } else { - return vd; - } -} - -//Returns a vector_desc entry for an source, the cpu parameter is used to tell GPIO_INT and GPIO_NMI from different CPUs -static vector_desc_t *find_desc_for_source(int source, int cpu) -{ - vector_desc_t *vd = vector_desc_head; - while (vd != NULL) { - if ( !(vd->flags & VECDESC_FL_SHARED) ) { - if ( vd->source == source && cpu == vd->cpu ) { - break; - } - } else if ( vd->cpu == cpu ) { - // check only shared vds for the correct cpu, otherwise skip - bool found = false; - shared_vector_desc_t *svd = vd->shared_vec_info; - assert(svd != NULL ); - while (svd) { - if ( svd->source == source ) { - found = true; - break; - } - svd = svd->next; - } - if ( found ) { - break; - } - } - vd = vd->next; - } - return vd; -} - -esp_err_t esp_intr_mark_shared(int intno, int cpu, bool is_int_ram) -{ - if (intno > 31) { - return ESP_ERR_INVALID_ARG; - } - if (cpu >= portNUM_PROCESSORS) { - return ESP_ERR_INVALID_ARG; - } - - portENTER_CRITICAL(&spinlock); - vector_desc_t *vd = get_desc_for_int(intno, cpu); - if (vd == NULL) { - portEXIT_CRITICAL(&spinlock); - return ESP_ERR_NO_MEM; - } - vd->flags = VECDESC_FL_SHARED; - if (is_int_ram) { - vd->flags |= VECDESC_FL_INIRAM; - } - portEXIT_CRITICAL(&spinlock); - - return ESP_OK; -} - -esp_err_t esp_intr_reserve(int intno, int cpu) -{ - if (intno > 31) { - return ESP_ERR_INVALID_ARG; - } - if (cpu >= portNUM_PROCESSORS) { - return ESP_ERR_INVALID_ARG; - } - - portENTER_CRITICAL(&spinlock); - vector_desc_t *vd = get_desc_for_int(intno, cpu); - if (vd == NULL) { - portEXIT_CRITICAL(&spinlock); - return ESP_ERR_NO_MEM; - } - vd->flags = VECDESC_FL_RESERVED; - portEXIT_CRITICAL(&spinlock); - - return ESP_OK; -} - -//Interrupt handler table and unhandled uinterrupt routine. Duplicated -//from xtensa_intr.c... it's supposed to be private, but we need to look -//into it in order to see if someone allocated an int using -//xt_set_interrupt_handler. -typedef struct xt_handler_table_entry { - void *handler; - void *arg; -} xt_handler_table_entry; -extern xt_handler_table_entry _xt_interrupt_table[XCHAL_NUM_INTERRUPTS * portNUM_PROCESSORS]; -extern void xt_unhandled_interrupt(void *arg); - -//Returns true if handler for interrupt is not the default unhandled interrupt handler -static bool int_has_handler(int intr, int cpu) -{ - return (_xt_interrupt_table[intr * portNUM_PROCESSORS + cpu].handler != xt_unhandled_interrupt); -} - -static bool is_vect_desc_usable(vector_desc_t *vd, int flags, int cpu, int force) -{ - //Check if interrupt is not reserved by design - int x = vd->intno; - if (int_desc[x].cpuflags[cpu] == INTDESC_RESVD) { - ALCHLOG("....Unusable: reserved"); - return false; - } - if (int_desc[x].cpuflags[cpu] == INTDESC_SPECIAL && force == -1) { - ALCHLOG("....Unusable: special-purpose int"); - return false; - } - //Check if the interrupt level is acceptable - if (!(flags & (1 << int_desc[x].level))) { - ALCHLOG("....Unusable: incompatible level"); - return false; - } - //check if edge/level type matches what we want - if (((flags & ESP_INTR_FLAG_EDGE) && (int_desc[x].type == INTTP_LEVEL)) || - (((!(flags & ESP_INTR_FLAG_EDGE)) && (int_desc[x].type == INTTP_EDGE)))) { - ALCHLOG("....Unusable: incompatible trigger type"); - return false; - } - //check if interrupt is reserved at runtime - if (vd->flags & VECDESC_FL_RESERVED) { - ALCHLOG("....Unusable: reserved at runtime."); - return false; - } - //Ints can't be both shared and non-shared. - assert(!((vd->flags & VECDESC_FL_SHARED) && (vd->flags & VECDESC_FL_NONSHARED))); - //check if interrupt already is in use by a non-shared interrupt - if (vd->flags & VECDESC_FL_NONSHARED) { - ALCHLOG("....Unusable: already in (non-shared) use."); - return false; - } - // check shared interrupt flags - if (vd->flags & VECDESC_FL_SHARED ) { - if (flags & ESP_INTR_FLAG_SHARED) { - bool in_iram_flag = ((flags & ESP_INTR_FLAG_IRAM) != 0); - bool desc_in_iram_flag = ((vd->flags & VECDESC_FL_INIRAM) != 0); - //Bail out if int is shared, but iram property doesn't match what we want. - if ((vd->flags & VECDESC_FL_SHARED) && (desc_in_iram_flag != in_iram_flag)) { - ALCHLOG("....Unusable: shared but iram prop doesn't match"); - return false; - } - } else { - //We need an unshared IRQ; can't use shared ones; bail out if this is shared. - ALCHLOG("...Unusable: int is shared, we need non-shared."); - return false; - } - } else if (int_has_handler(x, cpu)) { - //Check if interrupt already is allocated by xt_set_interrupt_handler - ALCHLOG("....Unusable: already allocated"); - return false; - } - return true; -} - -//Locate a free interrupt compatible with the flags given. -//The 'force' argument can be -1, or 0-31 to force checking a certain interrupt. -//When a CPU is forced, the INTDESC_SPECIAL marked interrupts are also accepted. -static int get_available_int(int flags, int cpu, int force, int source) -{ - int x; - int best = -1; - int bestLevel = 9; - int bestSharedCt = INT_MAX; - //Default vector desc, for vectors not in the linked list - vector_desc_t empty_vect_desc; - memset(&empty_vect_desc, 0, sizeof(vector_desc_t)); - - //Level defaults to any low/med interrupt - if (!(flags & ESP_INTR_FLAG_LEVELMASK)) { - flags |= ESP_INTR_FLAG_LOWMED; - } - - ALCHLOG("get_available_int: try to find existing. Cpu: %d, Source: %d", cpu, source); - vector_desc_t *vd = find_desc_for_source(source, cpu); - if ( vd ) { - // if existing vd found, don't need to search any more. - ALCHLOG("get_avalible_int: existing vd found. intno: %d", vd->intno); - if ( force != -1 && force != vd->intno ) { - ALCHLOG("get_avalible_int: intr forced but not matach existing. existing intno: %d, force: %d", vd->intno, force); - } else if ( !is_vect_desc_usable(vd, flags, cpu, force) ) { - ALCHLOG("get_avalible_int: existing vd invalid."); - } else { - best = vd->intno; - } - return best; - } - if (force != -1) { - ALCHLOG("get_available_int: try to find force. Cpu: %d, Source: %d, Force: %d", cpu, source, force); - //if force assigned, don't need to search any more. - vd = find_desc_for_int(force, cpu); - if (vd == NULL ) { - //if existing vd not found, just check the default state for the intr. - empty_vect_desc.intno = force; - vd = &empty_vect_desc; - } - if ( is_vect_desc_usable(vd, flags, cpu, force) ) { - best = vd->intno; - } else { - ALCHLOG("get_avalible_int: forced vd invalid."); - } - return best; - } - - ALCHLOG("get_free_int: start looking. Current cpu: %d", cpu); - //No allocated handlers as well as forced intr, iterate over the 32 possible interrupts - for (x = 0; x < 32; x++) { - //Grab the vector_desc for this vector. - vd = find_desc_for_int(x, cpu); - if (vd == NULL) { - empty_vect_desc.intno = x; - vd = &empty_vect_desc; - } - - ALCHLOG("Int %d reserved %d level %d %s hasIsr %d", - x, int_desc[x].cpuflags[cpu] == INTDESC_RESVD, int_desc[x].level, - int_desc[x].type == INTTP_LEVEL ? "LEVEL" : "EDGE", int_has_handler(x, cpu)); - if ( !is_vect_desc_usable(vd, flags, cpu, force) ) { - continue; - } - - if (flags & ESP_INTR_FLAG_SHARED) { - //We're allocating a shared int. - //See if int already is used as a shared interrupt. - if (vd->flags & VECDESC_FL_SHARED) { - //We can use this already-marked-as-shared interrupt. Count the already attached isrs in order to see - //how useful it is. - int no = 0; - shared_vector_desc_t *svdesc = vd->shared_vec_info; - while (svdesc != NULL) { - no++; - svdesc = svdesc->next; - } - if (no < bestSharedCt || bestLevel > int_desc[x].level) { - //Seems like this shared vector is both okay and has the least amount of ISRs already attached to it. - best = x; - bestSharedCt = no; - bestLevel = int_desc[x].level; - ALCHLOG("...int %d more usable as a shared int: has %d existing vectors", x, no); - } else { - ALCHLOG("...worse than int %d", best); - } - } else { - if (best == -1) { - //We haven't found a feasible shared interrupt yet. This one is still free and usable, even if - //not marked as shared. - //Remember it in case we don't find any other shared interrupt that qualifies. - if (bestLevel > int_desc[x].level) { - best = x; - bestLevel = int_desc[x].level; - ALCHLOG("...int %d usable as a new shared int", x); - } - } else { - ALCHLOG("...already have a shared int"); - } - } - } else { - //Seems this interrupt is feasible. Select it and break out of the loop; no need to search further. - if (bestLevel > int_desc[x].level) { - best = x; - bestLevel = int_desc[x].level; - } else { - ALCHLOG("...worse than int %d", best); - } - } - } - ALCHLOG("get_available_int: using int %d", best); - - //Okay, by now we have looked at all potential interrupts and hopefully have selected the best one in best. - return best; -} - -//Common shared isr handler. Chain-call all ISRs. -static void IRAM_ATTR shared_intr_isr(void *arg) -{ - vector_desc_t *vd = (vector_desc_t *)arg; - shared_vector_desc_t *sh_vec = vd->shared_vec_info; - portENTER_CRITICAL(&spinlock); - while (sh_vec) { - if (!sh_vec->disabled) { - if ((sh_vec->statusreg == NULL) || (*sh_vec->statusreg & sh_vec->statusmask)) { -#if CONFIG_SYSVIEW_ENABLE - traceISR_ENTER(sh_vec->source + ETS_INTERNAL_INTR_SOURCE_OFF); -#endif - sh_vec->isr(sh_vec->arg); -#if CONFIG_SYSVIEW_ENABLE - // check if we will return to scheduler or to interrupted task after ISR - if (!port_switch_flag[xPortGetCoreID()]) { - traceISR_EXIT(); - } -#endif - } - } - sh_vec = sh_vec->next; - } - portEXIT_CRITICAL(&spinlock); -} - -#if CONFIG_SYSVIEW_ENABLE -//Common non-shared isr handler wrapper. -static void IRAM_ATTR non_shared_intr_isr(void *arg) -{ - non_shared_isr_arg_t *ns_isr_arg = (non_shared_isr_arg_t *)arg; - portENTER_CRITICAL(&spinlock); - traceISR_ENTER(ns_isr_arg->source + ETS_INTERNAL_INTR_SOURCE_OFF); - // FIXME: can we call ISR and check port_switch_flag after releasing spinlock? - // when CONFIG_SYSVIEW_ENABLE = 0 ISRs for non-shared IRQs are called without spinlock - ns_isr_arg->isr(ns_isr_arg->isr_arg); - // check if we will return to scheduler or to interrupted task after ISR - if (!port_switch_flag[xPortGetCoreID()]) { - traceISR_EXIT(); - } - portEXIT_CRITICAL(&spinlock); -} -#endif - -//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) -{ - intr_handle_data_t *ret = NULL; - int force = -1; - ESP_EARLY_LOGV(TAG, "esp_intr_alloc_intrstatus (cpu %d): checking args", xPortGetCoreID()); - //Shared interrupts should be level-triggered. - if ((flags & ESP_INTR_FLAG_SHARED) && (flags & ESP_INTR_FLAG_EDGE)) { - return ESP_ERR_INVALID_ARG; - } - //You can't set an handler / arg for a non-C-callable interrupt. - if ((flags & ESP_INTR_FLAG_HIGH) && (handler)) { - return ESP_ERR_INVALID_ARG; - } - //Shared ints should have handler and non-processor-local source - if ((flags & ESP_INTR_FLAG_SHARED) && (!handler || source < 0)) { - return ESP_ERR_INVALID_ARG; - } - //Statusreg should have a mask - if (intrstatusreg && !intrstatusmask) { - return ESP_ERR_INVALID_ARG; - } - //If the ISR is marked to be IRAM-resident, the handler must not be in the cached region - if ((flags & ESP_INTR_FLAG_IRAM) && - (ptrdiff_t) handler >= SOC_RTC_IRAM_HIGH && - (ptrdiff_t) handler < SOC_RTC_DATA_LOW ) { - 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) { - flags |= ESP_INTR_FLAG_LEVEL1; - } else { - flags |= ESP_INTR_FLAG_LOWMED; - } - } - ESP_EARLY_LOGV(TAG, "esp_intr_alloc_intrstatus (cpu %d): Args okay. Resulting flags 0x%X", xPortGetCoreID(), flags); - - //Check 'special' interrupt sources. These are tied to one specific interrupt, so we - //have to force get_free_int to only look at that. - if (source == ETS_INTERNAL_TIMER0_INTR_SOURCE) { - force = ETS_INTERNAL_TIMER0_INTR_NO; - } - if (source == ETS_INTERNAL_TIMER1_INTR_SOURCE) { - force = ETS_INTERNAL_TIMER1_INTR_NO; - } - if (source == ETS_INTERNAL_TIMER2_INTR_SOURCE) { - force = ETS_INTERNAL_TIMER2_INTR_NO; - } - if (source == ETS_INTERNAL_SW0_INTR_SOURCE) { - force = ETS_INTERNAL_SW0_INTR_NO; - } - if (source == ETS_INTERNAL_SW1_INTR_SOURCE) { - force = ETS_INTERNAL_SW1_INTR_NO; - } - if (source == ETS_INTERNAL_PROFILING_INTR_SOURCE) { - force = ETS_INTERNAL_PROFILING_INTR_NO; - } - - //Allocate a return handle. If we end up not needing it, we'll free it later on. - ret = heap_caps_malloc(sizeof(intr_handle_data_t), MALLOC_CAP_INTERNAL | MALLOC_CAP_8BIT); - if (ret == NULL) { - return ESP_ERR_NO_MEM; - } - - portENTER_CRITICAL(&spinlock); - int cpu = xPortGetCoreID(); - //See if we can find an interrupt that matches the flags. - int intr = get_available_int(flags, cpu, force, source); - if (intr == -1) { - //None found. Bail out. - portEXIT_CRITICAL(&spinlock); - free(ret); - return ESP_ERR_NOT_FOUND; - } - //Get an int vector desc for int. - vector_desc_t *vd = get_desc_for_int(intr, cpu); - if (vd == NULL) { - portEXIT_CRITICAL(&spinlock); - free(ret); - return ESP_ERR_NO_MEM; - } - - //Allocate that int! - if (flags & ESP_INTR_FLAG_SHARED) { - //Populate vector entry and add to linked list. - shared_vector_desc_t *sh_vec = malloc(sizeof(shared_vector_desc_t)); - if (sh_vec == NULL) { - portEXIT_CRITICAL(&spinlock); - free(ret); - return ESP_ERR_NO_MEM; - } - memset(sh_vec, 0, sizeof(shared_vector_desc_t)); - sh_vec->statusreg = (uint32_t *)intrstatusreg; - sh_vec->statusmask = intrstatusmask; - sh_vec->isr = handler; - sh_vec->arg = arg; - sh_vec->next = vd->shared_vec_info; - sh_vec->source = source; - sh_vec->disabled = 0; - vd->shared_vec_info = sh_vec; - vd->flags |= VECDESC_FL_SHARED; - //(Re-)set shared isr handler to new value. - xt_set_interrupt_handler(intr, shared_intr_isr, vd); - } else { - //Mark as unusable for other interrupt sources. This is ours now! - vd->flags = VECDESC_FL_NONSHARED; - if (handler) { -#if CONFIG_SYSVIEW_ENABLE - non_shared_isr_arg_t *ns_isr_arg = malloc(sizeof(non_shared_isr_arg_t)); - if (!ns_isr_arg) { - portEXIT_CRITICAL(&spinlock); - free(ret); - return ESP_ERR_NO_MEM; - } - ns_isr_arg->isr = handler; - ns_isr_arg->isr_arg = arg; - ns_isr_arg->source = source; - xt_set_interrupt_handler(intr, non_shared_intr_isr, ns_isr_arg); -#else - xt_set_interrupt_handler(intr, handler, arg); -#endif - } - if (flags & ESP_INTR_FLAG_EDGE) { - xthal_set_intclear(1 << intr); - } - vd->source = source; - } - if (flags & ESP_INTR_FLAG_IRAM) { - vd->flags |= VECDESC_FL_INIRAM; - non_iram_int_mask[cpu] &= ~(1 << intr); - } else { - vd->flags &= ~VECDESC_FL_INIRAM; - non_iram_int_mask[cpu] |= (1 << intr); - } - if (source >= 0) { - intr_matrix_set(cpu, source, intr); - } - - //Fill return handle data. - ret->vector_desc = vd; - ret->shared_vector_desc = vd->shared_vec_info; - - //Enable int at CPU-level; - ESP_INTR_ENABLE(intr); - - //If interrupt has to be started disabled, do that now; ints won't be enabled for real until the end - //of the critical section. - if (flags & ESP_INTR_FLAG_INTRDISABLED) { - esp_intr_disable(ret); - } - - portEXIT_CRITICAL(&spinlock); - - //Fill return handle if needed, otherwise free handle. - if (ret_handle != NULL) { - *ret_handle = ret; - } else { - free(ret); - } - - ESP_EARLY_LOGD(TAG, "Connected src %d to int %d (cpu %d)", source, intr, cpu); - return ESP_OK; -} - -esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *arg, intr_handle_t *ret_handle) -{ - /* - As an optimization, we can create a table with the possible interrupt status registers and masks for every single - source there is. We can then add code here to look up an applicable value and pass that to the - esp_intr_alloc_intrstatus function. - */ - return esp_intr_alloc_intrstatus(source, flags, 0, 0, handler, arg, ret_handle); -} - -esp_err_t IRAM_ATTR esp_intr_set_in_iram(intr_handle_t handle, bool is_in_iram) -{ - if (!handle) { - return ESP_ERR_INVALID_ARG; - } - vector_desc_t *vd = handle->vector_desc; - if (vd->flags & VECDESC_FL_SHARED) { - return ESP_ERR_INVALID_ARG; - } - portENTER_CRITICAL(&spinlock); - uint32_t mask = (1 << vd->intno); - if (is_in_iram) { - vd->flags |= VECDESC_FL_INIRAM; - non_iram_int_mask[vd->cpu] &= ~mask; - } else { - vd->flags &= ~VECDESC_FL_INIRAM; - non_iram_int_mask[vd->cpu] |= mask; - } - portEXIT_CRITICAL(&spinlock); - return ESP_OK; -} - -esp_err_t esp_intr_free(intr_handle_t handle) -{ - bool free_shared_vector = false; - if (!handle) { - return ESP_ERR_INVALID_ARG; - } - //This routine should be called from the interrupt the task is scheduled on. - if (handle->vector_desc->cpu != xPortGetCoreID()) { - return ESP_ERR_INVALID_ARG; - } - - portENTER_CRITICAL(&spinlock); - esp_intr_disable(handle); - if (handle->vector_desc->flags & VECDESC_FL_SHARED) { - //Find and kill the shared int - shared_vector_desc_t *svd = handle->vector_desc->shared_vec_info; - shared_vector_desc_t *prevsvd = NULL; - assert(svd); //should be something in there for a shared int - while (svd != NULL) { - if (svd == handle->shared_vector_desc) { - //Found it. Now kill it. - if (prevsvd) { - prevsvd->next = svd->next; - } else { - handle->vector_desc->shared_vec_info = svd->next; - } - free(svd); - break; - } - prevsvd = svd; - svd = svd->next; - } - //If nothing left, disable interrupt. - if (handle->vector_desc->shared_vec_info == NULL) { - free_shared_vector = true; - } - ESP_LOGV(TAG, "esp_intr_free: Deleting shared int: %s. Shared int is %s", svd ? "not found or last one" : "deleted", free_shared_vector ? "empty now." : "still in use"); - } - - if ((handle->vector_desc->flags & VECDESC_FL_NONSHARED) || free_shared_vector) { - ESP_LOGV(TAG, "esp_intr_free: Disabling int, killing handler"); -#if CONFIG_SYSVIEW_ENABLE - if (!free_shared_vector) { - void *isr_arg = xt_get_interrupt_handler_arg(handle->vector_desc->intno); - if (isr_arg) { - free(isr_arg); - } - } -#endif - //Reset to normal handler - xt_set_interrupt_handler(handle->vector_desc->intno, xt_unhandled_interrupt, (void *)((int)handle->vector_desc->intno)); - //Theoretically, we could free the vector_desc... not sure if that's worth the few bytes of memory - //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); - //Also kill non_iram mask bit. - non_iram_int_mask[handle->vector_desc->cpu] &= ~(1 << (handle->vector_desc->intno)); - } - portEXIT_CRITICAL(&spinlock); - free(handle); - return ESP_OK; -} - -int esp_intr_get_intno(intr_handle_t handle) -{ - return handle->vector_desc->intno; -} - -int esp_intr_get_cpu(intr_handle_t handle) -{ - return handle->vector_desc->cpu; -} - -/* - Interrupt disabling strategy: - If the source is >=0 (meaning a muxed interrupt), we disable it by muxing the interrupt to a non-connected - interrupt. If the source is <0 (meaning an internal, per-cpu interrupt), we disable it using ESP_INTR_DISABLE. - This allows us to, for the muxed CPUs, disable an int from the other core. It also allows disabling shared - interrupts. - */ - -//Muxing an interrupt source to interrupt 6, 7, 11, 15, 16 or 29 cause the interrupt to effectively be disabled. -#define INT_MUX_DISABLED_INTNO 6 - -esp_err_t IRAM_ATTR esp_intr_enable(intr_handle_t handle) -{ - if (!handle) { - return ESP_ERR_INVALID_ARG; - } - portENTER_CRITICAL_SAFE(&spinlock); - int source; - if (handle->shared_vector_desc) { - handle->shared_vector_desc->disabled = 0; - source = handle->shared_vector_desc->source; - } else { - source = handle->vector_desc->source; - } - if (source >= 0) { - //Disabled using int matrix; re-connect to enable - intr_matrix_set(handle->vector_desc->cpu, source, handle->vector_desc->intno); - } else { - //Re-enable using cpu int ena reg - if (handle->vector_desc->cpu != xPortGetCoreID()) { - return ESP_ERR_INVALID_ARG; //Can only enable these ints on this cpu - } - ESP_INTR_ENABLE(handle->vector_desc->intno); - } - portEXIT_CRITICAL_SAFE(&spinlock); - return ESP_OK; -} - -esp_err_t IRAM_ATTR esp_intr_disable(intr_handle_t handle) -{ - if (!handle) { - return ESP_ERR_INVALID_ARG; - } - portENTER_CRITICAL_SAFE(&spinlock); - int source; - bool disabled = 1; - if (handle->shared_vector_desc) { - handle->shared_vector_desc->disabled = 1; - source = handle->shared_vector_desc->source; - - shared_vector_desc_t *svd = handle->vector_desc->shared_vec_info; - assert( svd != NULL ); - while ( svd ) { - if ( svd->source == source && svd->disabled == 0 ) { - disabled = 0; - break; - } - svd = svd->next; - } - } else { - source = handle->vector_desc->source; - } - - if (source >= 0) { - if ( disabled ) { - //Disable using int matrix - intr_matrix_set(handle->vector_desc->cpu, source, INT_MUX_DISABLED_INTNO); - } - } else { - //Disable using per-cpu regs - if (handle->vector_desc->cpu != xPortGetCoreID()) { - portEXIT_CRITICAL_SAFE(&spinlock); - return ESP_ERR_INVALID_ARG; //Can only enable these ints on this cpu - } - ESP_INTR_DISABLE(handle->vector_desc->intno); - } - portEXIT_CRITICAL_SAFE(&spinlock); - return ESP_OK; -} - - -void IRAM_ATTR esp_intr_noniram_disable(void) -{ - int oldint; - int cpu = xPortGetCoreID(); - int intmask = ~non_iram_int_mask[cpu]; - if (non_iram_int_disabled_flag[cpu]) { - abort(); - } - non_iram_int_disabled_flag[cpu] = true; - asm volatile ( - "movi %0,0\n" - "xsr %0,INTENABLE\n" //disable all ints first - "rsync\n" - "and a3,%0,%1\n" //mask ints that need disabling - "wsr a3,INTENABLE\n" //write back - "rsync\n" - :"=&r"(oldint):"r"(intmask):"a3"); - //Save which ints we did disable - non_iram_int_disabled[cpu] = oldint & non_iram_int_mask[cpu]; -} - -void IRAM_ATTR esp_intr_noniram_enable(void) -{ - int cpu = xPortGetCoreID(); - int intmask = non_iram_int_disabled[cpu]; - if (!non_iram_int_disabled_flag[cpu]) { - abort(); - } - non_iram_int_disabled_flag[cpu] = false; - asm volatile ( - "movi a3,0\n" - "xsr a3,INTENABLE\n" - "rsync\n" - "or a3,a3,%0\n" - "wsr a3,INTENABLE\n" - "rsync\n" - ::"r"(intmask):"a3"); -} - -//These functions are provided in ROM, but the ROM-based functions use non-multicore-capable -//virtualized interrupt levels. Thus, we disable them in the ld file and provide working -//equivalents here. - - -void IRAM_ATTR ets_isr_unmask(unsigned int mask) -{ - xt_ints_on(mask); -} - -void IRAM_ATTR ets_isr_mask(unsigned int mask) -{ - xt_ints_off(mask); -} diff --git a/components/esp_system/CMakeLists.txt b/components/esp_system/CMakeLists.txt index 80b6c542e7..1d19a5ffba 100644 --- a/components/esp_system/CMakeLists.txt +++ b/components/esp_system/CMakeLists.txt @@ -1,4 +1,4 @@ -idf_component_register(SRCS "esp_async_memcpy.c" "panic.c" "system_api.c" "startup.c" "sleep_modes.c" "system_time.c" +idf_component_register(SRCS "intr_alloc.c" "esp_async_memcpy.c" "panic.c" "system_api.c" "startup.c" "sleep_modes.c" "system_time.c" INCLUDE_DIRS include PRIV_REQUIRES spi_flash # [refactor-todo] requirements due to init code, diff --git a/components/esp32/include/esp_intr_alloc.h b/components/esp_system/include/esp_intr_alloc.h similarity index 94% rename from components/esp32/include/esp_intr_alloc.h rename to components/esp_system/include/esp_intr_alloc.h index 67aca5b779..ace95cbb5a 100644 --- a/components/esp32/include/esp_intr_alloc.h +++ b/components/esp_system/include/esp_intr_alloc.h @@ -18,7 +18,6 @@ #include #include #include "esp_err.h" -#include "freertos/xtensa_api.h" #ifdef __cplusplus extern "C" { @@ -68,9 +67,9 @@ extern "C" { * sources that do not pass through the interrupt mux. To allocate an interrupt for these sources, * pass these pseudo-sources to the functions. */ -#define ETS_INTERNAL_TIMER0_INTR_SOURCE -1 ///< Xtensa timer 0 interrupt source -#define ETS_INTERNAL_TIMER1_INTR_SOURCE -2 ///< Xtensa timer 1 interrupt source -#define ETS_INTERNAL_TIMER2_INTR_SOURCE -3 ///< Xtensa timer 2 interrupt source +#define ETS_INTERNAL_TIMER0_INTR_SOURCE -1 ///< Platform timer 0 interrupt source +#define ETS_INTERNAL_TIMER1_INTR_SOURCE -2 ///< Platform timer 1 interrupt source +#define ETS_INTERNAL_TIMER2_INTR_SOURCE -3 ///< Platform timer 2 interrupt source #define ETS_INTERNAL_SW0_INTR_SOURCE -4 ///< Software int source 1 #define ETS_INTERNAL_SW1_INTR_SOURCE -5 ///< Software int source 2 #define ETS_INTERNAL_PROFILING_INTR_SOURCE -6 ///< Int source for profiling @@ -82,10 +81,10 @@ extern "C" { #define ETS_INTERNAL_INTR_SOURCE_OFF (-ETS_INTERNAL_PROFILING_INTR_SOURCE) /** Enable interrupt by interrupt number */ -#define ESP_INTR_ENABLE(inum) xt_ints_on((1< #include #include #include #include +#include +#include +#include #include "freertos/FreeRTOS.h" #include "freertos/task.h" -#include #include "esp_err.h" -//#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE #include "esp_log.h" #include "esp_intr_alloc.h" #include "esp_attr.h" -#include -#include -#include "soc/soc.h" +#include "hal/cpu_hal.h" +#include "hal/interrupt_controller_hal.h" #if !CONFIG_FREERTOS_UNICORE #include "esp_ipc.h" #endif - static const char* TAG = "intr_alloc"; #define ETS_INTERNAL_TIMER0_INTR_NO 6 @@ -46,7 +43,6 @@ static const char* TAG = "intr_alloc"; #define ETS_INTERNAL_SW1_INTR_NO 29 #define ETS_INTERNAL_PROFILING_INTR_NO 11 - /* Define this to debug the choices made when allocating the interrupt. This leads to much debugging output within a critical region, which can lead to weird effects like e.g. the interrupt watchdog @@ -59,77 +55,6 @@ being triggered, that is why it is separate from the normal LOG* scheme. # define ALCHLOG(...) do {} while (0) #endif - -typedef enum { - INTDESC_NORMAL=0, - INTDESC_RESVD, - INTDESC_SPECIAL //for xtensa timers / software ints -} int_desc_flag_t; - -typedef enum { - INTTP_LEVEL=0, - INTTP_EDGE, - INTTP_NA -} int_type_t; - -typedef struct { - int level; - int_type_t type; - int_desc_flag_t cpuflags[2]; -} int_desc_t; - - -//We should mark the interrupt for the timer used by FreeRTOS as reserved. The specific timer -//is selectable using menuconfig; we use these cpp bits to convert that into something we can use in -//the table below. -#if CONFIG_FREERTOS_CORETIMER_0 -#define INT6RES INTDESC_RESVD -#else -#define INT6RES INTDESC_SPECIAL -#endif - -#if CONFIG_FREERTOS_CORETIMER_1 -#define INT15RES INTDESC_RESVD -#else -#define INT15RES INTDESC_SPECIAL -#endif - -//This is basically a software-readable version of the interrupt usage table in include/soc/soc.h -const static int_desc_t int_desc[32]={ - { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //0 - { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //1 - { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //2 - { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //3 - { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_NORMAL} }, //4 - { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //5 - { 1, INTTP_NA, {INT6RES, INT6RES } }, //6 - { 1, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL}}, //7 - { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //8 - { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //9 - { 1, INTTP_EDGE , {INTDESC_NORMAL, INTDESC_NORMAL} }, //10 - { 3, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL}}, //11 - { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //12 - { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //13 - { 7, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //14, NMI - { 3, INTTP_NA, {INT15RES, INT15RES } }, //15 - { 5, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL} }, //16 - { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //17 - { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //18 - { 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //19 - { 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //20 - { 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //21 - { 3, INTTP_EDGE, {INTDESC_RESVD, INTDESC_NORMAL} }, //22 - { 3, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //23 - { 4, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_NORMAL} }, //24 - { 4, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //25 - { 5, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_RESVD } }, //26 - { 3, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //27 - { 4, INTTP_EDGE, {INTDESC_NORMAL, INTDESC_NORMAL} }, //28 - { 3, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL}}, //29 - { 4, INTTP_EDGE, {INTDESC_RESVD, INTDESC_RESVD } }, //30 - { 5, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //31 -}; - typedef struct shared_vector_desc_t shared_vector_desc_t; typedef struct vector_desc_t vector_desc_t; @@ -143,7 +68,6 @@ struct shared_vector_desc_t { shared_vector_desc_t *next; }; - #define VECDESC_FL_RESERVED (1<<0) #define VECDESC_FL_INIRAM (1<<1) #define VECDESC_FL_SHARED (1<<2) @@ -176,10 +100,11 @@ struct non_shared_isr_arg_t { static vector_desc_t *vector_desc_head = NULL; //This bitmask has an 1 if the int should be disabled when the flash is disabled. -static uint32_t non_iram_int_mask[portNUM_PROCESSORS]; +static uint32_t non_iram_int_mask[SOC_CPU_CORES_NUM]; + //This bitmask has 1 in it if the int was disabled using esp_intr_noniram_disable. -static uint32_t non_iram_int_disabled[portNUM_PROCESSORS]; -static bool non_iram_int_disabled_flag[portNUM_PROCESSORS]; +static uint32_t non_iram_int_disabled[SOC_CPU_CORES_NUM]; +static bool non_iram_int_disabled_flag[SOC_CPU_CORES_NUM]; #if CONFIG_SYSVIEW_ENABLE extern uint32_t port_switch_flag[]; @@ -268,7 +193,7 @@ static vector_desc_t * find_desc_for_source(int source, int cpu) esp_err_t esp_intr_mark_shared(int intno, int cpu, bool is_int_ram) { if (intno>31) return ESP_ERR_INVALID_ARG; - if (cpu>=portNUM_PROCESSORS) return ESP_ERR_INVALID_ARG; + if (cpu>=SOC_CPU_CORES_NUM) return ESP_ERR_INVALID_ARG; portENTER_CRITICAL(&spinlock); vector_desc_t *vd=get_desc_for_int(intno, cpu); @@ -286,7 +211,7 @@ esp_err_t esp_intr_mark_shared(int intno, int cpu, bool is_int_ram) esp_err_t esp_intr_reserve(int intno, int cpu) { if (intno>31) return ESP_ERR_INVALID_ARG; - if (cpu>=portNUM_PROCESSORS) return ESP_ERR_INVALID_ARG; + if (cpu>=SOC_CPU_CORES_NUM) return ESP_ERR_INVALID_ARG; portENTER_CRITICAL(&spinlock); vector_desc_t *vd=get_desc_for_int(intno, cpu); @@ -300,43 +225,26 @@ esp_err_t esp_intr_reserve(int intno, int cpu) return ESP_OK; } -//Interrupt handler table and unhandled uinterrupt routine. Duplicated -//from xtensa_intr.c... it's supposed to be private, but we need to look -//into it in order to see if someone allocated an int using -//xt_set_interrupt_handler. -typedef struct xt_handler_table_entry { - void * handler; - void * arg; -} xt_handler_table_entry; -extern xt_handler_table_entry _xt_interrupt_table[XCHAL_NUM_INTERRUPTS*portNUM_PROCESSORS]; -extern void xt_unhandled_interrupt(void * arg); - -//Returns true if handler for interrupt is not the default unhandled interrupt handler -static bool int_has_handler(int intr, int cpu) -{ - return (_xt_interrupt_table[intr*portNUM_PROCESSORS+cpu].handler != xt_unhandled_interrupt); -} - static bool is_vect_desc_usable(vector_desc_t *vd, int flags, int cpu, int force) { //Check if interrupt is not reserved by design int x = vd->intno; - if (int_desc[x].cpuflags[cpu]==INTDESC_RESVD) { + if (interrupt_controller_hal_get_cpu_desc_flags(x, cpu)==INTDESC_RESVD) { ALCHLOG("....Unusable: reserved"); return false; } - if (int_desc[x].cpuflags[cpu]==INTDESC_SPECIAL && force==-1) { + if (interrupt_controller_hal_get_cpu_desc_flags(x, cpu)==INTDESC_SPECIAL && force==-1) { ALCHLOG("....Unusable: special-purpose int"); return false; } //Check if the interrupt level is acceptable - if (!(flags&(1<next; } - if (noint_desc[x].level) { + if (nointerrupt_controller_hal_get_level(x)) { //Seems like this shared vector is both okay and has the least amount of ISRs already attached to it. best=x; bestSharedCt=no; - bestLevel=int_desc[x].level; + bestLevel=interrupt_controller_hal_get_level(x); ALCHLOG("...int %d more usable as a shared int: has %d existing vectors", x, no); } else { ALCHLOG("...worse than int %d", best); @@ -468,9 +376,9 @@ static int get_available_int(int flags, int cpu, int force, int source) //We haven't found a feasible shared interrupt yet. This one is still free and usable, even if //not marked as shared. //Remember it in case we don't find any other shared interrupt that qualifies. - if (bestLevel>int_desc[x].level) { + if (bestLevel>interrupt_controller_hal_get_level(x)) { best=x; - bestLevel=int_desc[x].level; + bestLevel=interrupt_controller_hal_get_level(x); ALCHLOG("...int %d usable as a new shared int", x); } } else { @@ -479,9 +387,9 @@ static int get_available_int(int flags, int cpu, int force, int source) } } else { //Seems this interrupt is feasible. Select it and break out of the loop; no need to search further. - if (bestLevel>int_desc[x].level) { + if (bestLevel>interrupt_controller_hal_get_level(x)) { best=x; - bestLevel=int_desc[x].level; + bestLevel=interrupt_controller_hal_get_level(x); } else { ALCHLOG("...worse than int %d", best); } @@ -508,7 +416,7 @@ static void IRAM_ATTR shared_intr_isr(void *arg) sh_vec->isr(sh_vec->arg); #if CONFIG_SYSVIEW_ENABLE // check if we will return to scheduler or to interrupted task after ISR - if (!port_switch_flag[xPortGetCoreID()]) { + if (!port_switch_flag[cpu_hal_get_core_id()]) { traceISR_EXIT(); } #endif @@ -530,7 +438,7 @@ static void IRAM_ATTR non_shared_intr_isr(void *arg) // when CONFIG_SYSVIEW_ENABLE = 0 ISRs for non-shared IRQs are called without spinlock ns_isr_arg->isr(ns_isr_arg->isr_arg); // check if we will return to scheduler or to interrupted task after ISR - if (!port_switch_flag[xPortGetCoreID()]) { + if (!port_switch_flag[cpu_hal_get_core_id()]) { traceISR_EXIT(); } portEXIT_CRITICAL_ISR(&spinlock); @@ -543,7 +451,7 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre { intr_handle_data_t *ret=NULL; int force=-1; - ESP_EARLY_LOGV(TAG, "esp_intr_alloc_intrstatus (cpu %d): checking args", xPortGetCoreID()); + ESP_EARLY_LOGV(TAG, "esp_intr_alloc_intrstatus (cpu %d): checking args", cpu_hal_get_core_id()); //Shared interrupts should be level-triggered. if ((flags&ESP_INTR_FLAG_SHARED) && (flags&ESP_INTR_FLAG_EDGE)) return ESP_ERR_INVALID_ARG; //You can't set an handler / arg for a non-C-callable interrupt. @@ -570,7 +478,7 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre flags|=ESP_INTR_FLAG_LOWMED; } } - ESP_EARLY_LOGV(TAG, "esp_intr_alloc_intrstatus (cpu %d): Args okay. Resulting flags 0x%X", xPortGetCoreID(), flags); + ESP_EARLY_LOGV(TAG, "esp_intr_alloc_intrstatus (cpu %d): Args okay. Resulting flags 0x%X", cpu_hal_get_core_id(), flags); //Check 'special' interrupt sources. These are tied to one specific interrupt, so we //have to force get_free_int to only look at that. @@ -586,7 +494,7 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre if (ret==NULL) return ESP_ERR_NO_MEM; portENTER_CRITICAL(&spinlock); - int cpu=xPortGetCoreID(); + int cpu=cpu_hal_get_core_id(); //See if we can find an interrupt that matches the flags. int intr=get_available_int(flags, cpu, force, source); if (intr==-1) { @@ -638,9 +546,9 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre ns_isr_arg->isr=handler; ns_isr_arg->isr_arg=arg; ns_isr_arg->source=source; - xt_set_interrupt_handler(intr, non_shared_intr_isr, ns_isr_arg); + interrupt_controller_hal_set_int_handler(intr, non_shared_intr_isr, ns_isr_arg); #else - xt_set_interrupt_handler(intr, handler, arg); + interrupt_controller_hal_set_int_handler(intr, handler, arg); #endif } if (flags&ESP_INTR_FLAG_EDGE) xthal_set_intclear(1 << intr); @@ -727,7 +635,7 @@ esp_err_t esp_intr_free(intr_handle_t handle) #if !CONFIG_FREERTOS_UNICORE //Assign this routine to the core where this interrupt is allocated on. - if (handle->vector_desc->cpu!=xPortGetCoreID()) { + if (handle->vector_desc->cpu!=cpu_hal_get_core_id()) { esp_err_t ret = esp_ipc_call_blocking(handle->vector_desc->cpu, &esp_intr_free_cb, (void *)handle); return ret == ESP_OK ? ESP_OK : ESP_FAIL; } @@ -763,14 +671,14 @@ esp_err_t esp_intr_free(intr_handle_t handle) ESP_EARLY_LOGV(TAG, "esp_intr_free: Disabling int, killing handler"); #if CONFIG_SYSVIEW_ENABLE if (!free_shared_vector) { - void *isr_arg = xt_get_interrupt_handler_arg(handle->vector_desc->intno); + void *isr_arg = interrupt_controller_hal_get_int_handler_arg(handle->vector_desc->intno); if (isr_arg) { free(isr_arg); } } #endif - //Reset to normal handler - xt_set_interrupt_handler(handle->vector_desc->intno, xt_unhandled_interrupt, (void*)((int)handle->vector_desc->intno)); + //Reset to normal handler: + interrupt_controller_hal_set_int_handler(handle->vector_desc->intno, NULL, (void*)((int)handle->vector_desc->intno)); //Theoretically, we could free the vector_desc... not sure if that's worth the few bytes of memory //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. @@ -820,7 +728,7 @@ esp_err_t IRAM_ATTR esp_intr_enable(intr_handle_t handle) intr_matrix_set(handle->vector_desc->cpu, source, handle->vector_desc->intno); } else { //Re-enable using cpu int ena reg - if (handle->vector_desc->cpu!=xPortGetCoreID()) return ESP_ERR_INVALID_ARG; //Can only enable these ints on this cpu + if (handle->vector_desc->cpu!=cpu_hal_get_core_id()) return ESP_ERR_INVALID_ARG; //Can only enable these ints on this cpu ESP_INTR_ENABLE(handle->vector_desc->intno); } portEXIT_CRITICAL_SAFE(&spinlock); @@ -857,7 +765,7 @@ esp_err_t IRAM_ATTR esp_intr_disable(intr_handle_t handle) } } else { //Disable using per-cpu regs - if (handle->vector_desc->cpu!=xPortGetCoreID()) { + if (handle->vector_desc->cpu!=cpu_hal_get_core_id()) { portEXIT_CRITICAL_SAFE(&spinlock); return ESP_ERR_INVALID_ARG; //Can only enable these ints on this cpu } @@ -867,40 +775,25 @@ esp_err_t IRAM_ATTR esp_intr_disable(intr_handle_t handle) return ESP_OK; } - void IRAM_ATTR esp_intr_noniram_disable(void) { - int oldint; - int cpu=xPortGetCoreID(); - int intmask=~non_iram_int_mask[cpu]; + uint32_t oldint; + int cpu=cpu_hal_get_core_id(); + uint32_t intmask=~non_iram_int_mask[cpu]; if (non_iram_int_disabled_flag[cpu]) abort(); non_iram_int_disabled_flag[cpu]=true; - asm volatile ( - "movi %0,0\n" - "xsr %0,INTENABLE\n" //disable all ints first - "rsync\n" - "and a3,%0,%1\n" //mask ints that need disabling - "wsr a3,INTENABLE\n" //write back - "rsync\n" - :"=&r"(oldint):"r"(intmask):"a3"); + oldint = interrupt_controller_hal_disable_int_mask(intmask); //Save which ints we did disable non_iram_int_disabled[cpu]=oldint&non_iram_int_mask[cpu]; } void IRAM_ATTR esp_intr_noniram_enable(void) { - int cpu=xPortGetCoreID(); + int cpu=cpu_hal_get_core_id(); int intmask=non_iram_int_disabled[cpu]; if (!non_iram_int_disabled_flag[cpu]) abort(); non_iram_int_disabled_flag[cpu]=false; - asm volatile ( - "movi a3,0\n" - "xsr a3,INTENABLE\n" - "rsync\n" - "or a3,a3,%0\n" - "wsr a3,INTENABLE\n" - "rsync\n" - ::"r"(intmask):"a3"); + interrupt_controller_hal_enable_int_mask(intmask); } //These functions are provided in ROM, but the ROM-based functions use non-multicore-capable @@ -909,13 +802,19 @@ void IRAM_ATTR esp_intr_noniram_enable(void) void IRAM_ATTR ets_isr_unmask(unsigned int mask) { - xt_ints_on(mask); + interrupt_controller_hal_enable_interrupts(mask); } void IRAM_ATTR ets_isr_mask(unsigned int mask) { - xt_ints_off(mask); + interrupt_controller_hal_disable_interrupts(mask); } +void esp_intr_enable_source(int inum) +{ + interrupt_controller_hal_enable_interrupts(1 << inum); +} - - +void esp_intr_disable_source(int inum) +{ + interrupt_controller_hal_disable_interrupts(1 << inum); +} \ No newline at end of file diff --git a/components/esp32/test/test_intr_alloc.c b/components/esp_system/test/test_intr_alloc.c similarity index 95% rename from components/esp32/test/test_intr_alloc.c rename to components/esp_system/test/test_intr_alloc.c index 12531fb7f0..e6bb963da3 100644 --- a/components/esp32/test/test_intr_alloc.c +++ b/components/esp_system/test/test_intr_alloc.c @@ -17,7 +17,7 @@ #include "esp_intr_alloc.h" #include "driver/periph_ctrl.h" #include "driver/timer.h" - +#include "sdkconfig.h" #define TIMER_DIVIDER 16 /*!< Hardware timer clock divider */ #define TIMER_SCALE (TIMER_BASE_CLK / TIMER_DIVIDER) /*!< used to calculate counter value */ @@ -242,7 +242,12 @@ void IRAM_ATTR int_handler1(void* arg) } else { ctx->flag1 = true; } + + #ifdef CONFIG_IDF_TARGET_ESP32 SPI2.slave.trans_done = 0; + #else + GPSPI2.slave.trans_done = 0; + #endif } void IRAM_ATTR int_handler2(void* arg) @@ -260,9 +265,13 @@ TEST_CASE("allocate 2 handlers for a same source and remove the later one","[int { intr_alloc_test_ctx_t ctx = {false, false, false, false }; intr_handle_t handle1, handle2; - + + #ifdef CONFIG_IDF_TARGET_ESP32 //enable HSPI(spi2) periph_module_enable(PERIPH_HSPI_MODULE); + #else + periph_module_enable(PERIPH_FSPI_MODULE); + #endif esp_err_t r; r=esp_intr_alloc(ETS_SPI2_INTR_SOURCE, ESP_INTR_FLAG_SHARED, int_handler1, &ctx, &handle1); @@ -273,10 +282,20 @@ TEST_CASE("allocate 2 handlers for a same source and remove the later one","[int //assign shared then r=esp_intr_alloc(ETS_SPI2_INTR_SOURCE, ESP_INTR_FLAG_SHARED, int_handler2, &ctx, &handle2); TEST_ESP_OK(r); + + #ifdef CONFIG_IDF_TARGET_ESP32 SPI2.slave.trans_inten = 1; + #else + GPSPI2.slave.int_trans_done_en = 1; + #endif printf("trigger first time.\n"); + + #ifdef CONFIG_IDF_TARGET_ESP32 SPI2.slave.trans_done = 1; + #else + GPSPI2.slave.trans_done = 1; + #endif vTaskDelay(100); TEST_ASSERT( ctx.flag1 && ctx.flag2 ); @@ -285,13 +304,19 @@ TEST_CASE("allocate 2 handlers for a same source and remove the later one","[int r=esp_intr_free(handle2); printf("trigger second time.\n"); + + #ifdef CONFIG_IDF_TARGET_ESP32 SPI2.slave.trans_done = 1; + #else + GPSPI2.slave.trans_done = 1; + #endif vTaskDelay(500); TEST_ASSERT( ctx.flag3 && !ctx.flag4 ); printf("test passed.\n"); } + #ifndef CONFIG_FREERTOS_UNICORE void isr_free_task(void *param) diff --git a/components/freertos/CMakeLists.txt b/components/freertos/CMakeLists.txt index 5ca3475825..80c3ae902a 100644 --- a/components/freertos/CMakeLists.txt +++ b/components/freertos/CMakeLists.txt @@ -9,8 +9,6 @@ set(srcs "xtensa/portasm.S" "xtensa/xtensa_context.S" "xtensa/xtensa_init.c" - "xtensa/xtensa_intr_asm.S" - "xtensa/xtensa_intr.c" "xtensa/xtensa_overlay_os_hook.c" "xtensa/xtensa_vector_defaults.S" "xtensa/xtensa_vectors.S") diff --git a/components/freertos/xtensa/include/freertos/portmacro.h b/components/freertos/xtensa/include/freertos/portmacro.h index e4d2c057b0..fb0b6b72b5 100644 --- a/components/freertos/xtensa/include/freertos/portmacro.h +++ b/components/freertos/xtensa/include/freertos/portmacro.h @@ -86,13 +86,12 @@ extern "C" { #include #include "esp_rom_sys.h" #include "sdkconfig.h" +#include "freertos/xtensa_api.h" #ifdef CONFIG_LEGACY_INCLUDE_COMMON_HEADERS #include "soc/soc_memory_layout.h" #endif -//#include "xtensa_context.h" - /*----------------------------------------------------------- * Port specific definitions. * diff --git a/components/freertos/xtensa/include/freertos/xtensa_api.h b/components/freertos/xtensa/include/freertos/xtensa_api.h index 19630ce581..bdfd7151c8 100644 --- a/components/freertos/xtensa/include/freertos/xtensa_api.h +++ b/components/freertos/xtensa/include/freertos/xtensa_api.h @@ -1,130 +1,2 @@ -/******************************************************************************* -Copyright (c) 2006-2015 Cadence Design Systems Inc. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -******************************************************************************/ - -/****************************************************************************** - Xtensa-specific API for RTOS ports. -******************************************************************************/ - -#ifndef __XTENSA_API_H__ -#define __XTENSA_API_H__ - -#include - -#include "xtensa_context.h" - - -/* Typedef for C-callable interrupt handler function */ -typedef void (*xt_handler)(void *); - -/* Typedef for C-callable exception handler function */ -typedef void (*xt_exc_handler)(XtExcFrame *); - - -/* -------------------------------------------------------------------------------- - Call this function to set a handler for the specified exception. The handler - will be installed on the core that calls this function. - - n - Exception number (type) - f - Handler function address, NULL to uninstall handler. - - The handler will be passed a pointer to the exception frame, which is created - on the stack of the thread that caused the exception. - - If the handler returns, the thread context will be restored and the faulting - instruction will be retried. Any values in the exception frame that are - modified by the handler will be restored as part of the context. For details - of the exception frame structure see xtensa_context.h. -------------------------------------------------------------------------------- -*/ -extern xt_exc_handler xt_set_exception_handler(int n, xt_exc_handler f); - - -/* -------------------------------------------------------------------------------- - Call this function to set a handler for the specified interrupt. The handler - will be installed on the core that calls this function. - - n - Interrupt number. - f - Handler function address, NULL to uninstall handler. - arg - Argument to be passed to handler. -------------------------------------------------------------------------------- -*/ -extern xt_handler xt_set_interrupt_handler(int n, xt_handler f, void * arg); - - -/* -------------------------------------------------------------------------------- - Call this function to enable the specified interrupts on the core that runs - this code. - - mask - Bit mask of interrupts to be enabled. -------------------------------------------------------------------------------- -*/ -extern void xt_ints_on(unsigned int mask); - - -/* -------------------------------------------------------------------------------- - Call this function to disable the specified interrupts on the core that runs - this code. - - mask - Bit mask of interrupts to be disabled. -------------------------------------------------------------------------------- -*/ -extern void xt_ints_off(unsigned int mask); - - -/* -------------------------------------------------------------------------------- - Call this function to set the specified (s/w) interrupt. -------------------------------------------------------------------------------- -*/ -static inline void xt_set_intset(unsigned int arg) -{ - xthal_set_intset(arg); -} - - -/* -------------------------------------------------------------------------------- - Call this function to clear the specified (s/w or edge-triggered) - interrupt. -------------------------------------------------------------------------------- -*/ -static inline void xt_set_intclear(unsigned int arg) -{ - xthal_set_intclear(arg); -} - -/* -------------------------------------------------------------------------------- - Call this function to get handler's argument for the specified interrupt. - - n - Interrupt number. -------------------------------------------------------------------------------- -*/ -extern void * xt_get_interrupt_handler_arg(int n); - -#endif /* __XTENSA_API_H__ */ - +/* This header file has been moved, please include in future */ +#include diff --git a/components/freertos/xtensa/include/freertos/xtensa_context.h b/components/freertos/xtensa/include/freertos/xtensa_context.h index 120676dad4..1d0f4e584a 100644 --- a/components/freertos/xtensa/include/freertos/xtensa_context.h +++ b/components/freertos/xtensa/include/freertos/xtensa_context.h @@ -1,387 +1,2 @@ -/******************************************************************************* -Copyright (c) 2006-2015 Cadence Design Systems Inc. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be included -in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. -IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY -CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, -TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE -SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------------------------------- - - XTENSA CONTEXT FRAMES AND MACROS FOR RTOS ASSEMBLER SOURCES - -This header contains definitions and macros for use primarily by Xtensa -RTOS assembly coded source files. It includes and uses the Xtensa hardware -abstraction layer (HAL) to deal with config specifics. It may also be -included in C source files. - -!! Supports only Xtensa Exception Architecture 2 (XEA2). XEA1 not supported. !! - -NOTE: The Xtensa architecture requires stack pointer alignment to 16 bytes. - -*******************************************************************************/ - -#ifndef XTENSA_CONTEXT_H -#define XTENSA_CONTEXT_H - -#ifdef __ASSEMBLER__ -#include -#endif - -#include -#include -#include -#include - - -/* Align a value up to nearest n-byte boundary, where n is a power of 2. */ -#define ALIGNUP(n, val) (((val) + (n)-1) & -(n)) - - -/* -------------------------------------------------------------------------------- - Macros that help define structures for both C and assembler. -------------------------------------------------------------------------------- -*/ - -#ifdef STRUCT_BEGIN -#undef STRUCT_BEGIN -#undef STRUCT_FIELD -#undef STRUCT_AFIELD -#undef STRUCT_END -#endif - -#if defined(_ASMLANGUAGE) || defined(__ASSEMBLER__) - -#define STRUCT_BEGIN .pushsection .text; .struct 0 -#define STRUCT_FIELD(ctype,size,asname,name) asname: .space size -#define STRUCT_AFIELD(ctype,size,asname,name,n) asname: .space (size)*(n) -#define STRUCT_END(sname) sname##Size:; .popsection - -#else - -#define STRUCT_BEGIN typedef struct { -#define STRUCT_FIELD(ctype,size,asname,name) ctype name; -#define STRUCT_AFIELD(ctype,size,asname,name,n) ctype name[n]; -#define STRUCT_END(sname) } sname; - -#endif //_ASMLANGUAGE || __ASSEMBLER__ - - -/* -------------------------------------------------------------------------------- - INTERRUPT/EXCEPTION STACK FRAME FOR A THREAD OR NESTED INTERRUPT - - A stack frame of this structure is allocated for any interrupt or exception. - It goes on the current stack. If the RTOS has a system stack for handling - interrupts, every thread stack must allow space for just one interrupt stack - frame, then nested interrupt stack frames go on the system stack. - - The frame includes basic registers (explicit) and "extra" registers introduced - by user TIE or the use of the MAC16 option in the user's Xtensa config. - The frame size is minimized by omitting regs not applicable to user's config. - - For Windowed ABI, this stack frame includes the interruptee's base save area, - another base save area to manage gcc nested functions, and a little temporary - space to help manage the spilling of the register windows. -------------------------------------------------------------------------------- -*/ - -STRUCT_BEGIN -STRUCT_FIELD (long, 4, XT_STK_EXIT, exit) /* exit point for dispatch */ -STRUCT_FIELD (long, 4, XT_STK_PC, pc) /* return PC */ -STRUCT_FIELD (long, 4, XT_STK_PS, ps) /* return PS */ -STRUCT_FIELD (long, 4, XT_STK_A0, a0) -STRUCT_FIELD (long, 4, XT_STK_A1, a1) /* stack pointer before interrupt */ -STRUCT_FIELD (long, 4, XT_STK_A2, a2) -STRUCT_FIELD (long, 4, XT_STK_A3, a3) -STRUCT_FIELD (long, 4, XT_STK_A4, a4) -STRUCT_FIELD (long, 4, XT_STK_A5, a5) -STRUCT_FIELD (long, 4, XT_STK_A6, a6) -STRUCT_FIELD (long, 4, XT_STK_A7, a7) -STRUCT_FIELD (long, 4, XT_STK_A8, a8) -STRUCT_FIELD (long, 4, XT_STK_A9, a9) -STRUCT_FIELD (long, 4, XT_STK_A10, a10) -STRUCT_FIELD (long, 4, XT_STK_A11, a11) -STRUCT_FIELD (long, 4, XT_STK_A12, a12) -STRUCT_FIELD (long, 4, XT_STK_A13, a13) -STRUCT_FIELD (long, 4, XT_STK_A14, a14) -STRUCT_FIELD (long, 4, XT_STK_A15, a15) -STRUCT_FIELD (long, 4, XT_STK_SAR, sar) -STRUCT_FIELD (long, 4, XT_STK_EXCCAUSE, exccause) -STRUCT_FIELD (long, 4, XT_STK_EXCVADDR, excvaddr) -#if XCHAL_HAVE_LOOPS -STRUCT_FIELD (long, 4, XT_STK_LBEG, lbeg) -STRUCT_FIELD (long, 4, XT_STK_LEND, lend) -STRUCT_FIELD (long, 4, XT_STK_LCOUNT, lcount) -#endif -#ifndef __XTENSA_CALL0_ABI__ -/* Temporary space for saving stuff during window spill */ -STRUCT_FIELD (long, 4, XT_STK_TMP0, tmp0) -STRUCT_FIELD (long, 4, XT_STK_TMP1, tmp1) -STRUCT_FIELD (long, 4, XT_STK_TMP2, tmp2) -#endif -#ifdef XT_USE_SWPRI -/* Storage for virtual priority mask */ -STRUCT_FIELD (long, 4, XT_STK_VPRI, vpri) -#endif -#ifdef XT_USE_OVLY -/* Storage for overlay state */ -STRUCT_FIELD (long, 4, XT_STK_OVLY, ovly) -#endif -STRUCT_END(XtExcFrame) - -#if defined(_ASMLANGUAGE) || defined(__ASSEMBLER__) -#define XT_STK_NEXT1 XtExcFrameSize -#else -#define XT_STK_NEXT1 sizeof(XtExcFrame) -#endif - -/* Allocate extra storage if needed */ -#if XCHAL_EXTRA_SA_SIZE != 0 - -#if XCHAL_EXTRA_SA_ALIGN <= 16 -#define XT_STK_EXTRA ALIGNUP(XCHAL_EXTRA_SA_ALIGN, XT_STK_NEXT1) -#else -/* If need more alignment than stack, add space for dynamic alignment */ -#define XT_STK_EXTRA (ALIGNUP(XCHAL_EXTRA_SA_ALIGN, XT_STK_NEXT1) + XCHAL_EXTRA_SA_ALIGN) -#endif -#define XT_STK_NEXT2 (XT_STK_EXTRA + XCHAL_EXTRA_SA_SIZE) - -#else - -#define XT_STK_NEXT2 XT_STK_NEXT1 - -#endif - -/* -------------------------------------------------------------------------------- - This is the frame size. Add space for 4 registers (interruptee's base save - area) and some space for gcc nested functions if any. -------------------------------------------------------------------------------- -*/ -#define XT_STK_FRMSZ (ALIGNUP(0x10, XT_STK_NEXT2) + 0x20) - - -/* -------------------------------------------------------------------------------- - SOLICITED STACK FRAME FOR A THREAD - - A stack frame of this structure is allocated whenever a thread enters the - RTOS kernel intentionally (and synchronously) to submit to thread scheduling. - It goes on the current thread's stack. - - The solicited frame only includes registers that are required to be preserved - by the callee according to the compiler's ABI conventions, some space to save - the return address for returning to the caller, and the caller's PS register. - - For Windowed ABI, this stack frame includes the caller's base save area. - - Note on XT_SOL_EXIT field: - It is necessary to distinguish a solicited from an interrupt stack frame. - This field corresponds to XT_STK_EXIT in the interrupt stack frame and is - always at the same offset (0). It can be written with a code (usually 0) - to distinguish a solicted frame from an interrupt frame. An RTOS port may - opt to ignore this field if it has another way of distinguishing frames. -------------------------------------------------------------------------------- -*/ - -STRUCT_BEGIN -#ifdef __XTENSA_CALL0_ABI__ -STRUCT_FIELD (long, 4, XT_SOL_EXIT, exit) -STRUCT_FIELD (long, 4, XT_SOL_PC, pc) -STRUCT_FIELD (long, 4, XT_SOL_PS, ps) -STRUCT_FIELD (long, 4, XT_SOL_NEXT, next) -STRUCT_FIELD (long, 4, XT_SOL_A12, a12) /* should be on 16-byte alignment */ -STRUCT_FIELD (long, 4, XT_SOL_A13, a13) -STRUCT_FIELD (long, 4, XT_SOL_A14, a14) -STRUCT_FIELD (long, 4, XT_SOL_A15, a15) -#else -STRUCT_FIELD (long, 4, XT_SOL_EXIT, exit) -STRUCT_FIELD (long, 4, XT_SOL_PC, pc) -STRUCT_FIELD (long, 4, XT_SOL_PS, ps) -STRUCT_FIELD (long, 4, XT_SOL_NEXT, next) -STRUCT_FIELD (long, 4, XT_SOL_A0, a0) /* should be on 16-byte alignment */ -STRUCT_FIELD (long, 4, XT_SOL_A1, a1) -STRUCT_FIELD (long, 4, XT_SOL_A2, a2) -STRUCT_FIELD (long, 4, XT_SOL_A3, a3) -#endif -STRUCT_END(XtSolFrame) - -/* Size of solicited stack frame */ -#define XT_SOL_FRMSZ ALIGNUP(0x10, XtSolFrameSize) - - -/* -------------------------------------------------------------------------------- - CO-PROCESSOR STATE SAVE AREA FOR A THREAD - - The RTOS must provide an area per thread to save the state of co-processors - when that thread does not have control. Co-processors are context-switched - lazily (on demand) only when a new thread uses a co-processor instruction, - otherwise a thread retains ownership of the co-processor even when it loses - control of the processor. An Xtensa co-processor exception is triggered when - any co-processor instruction is executed by a thread that is not the owner, - and the context switch of that co-processor is then peformed by the handler. - Ownership represents which thread's state is currently in the co-processor. - - Co-processors may not be used by interrupt or exception handlers. If an - co-processor instruction is executed by an interrupt or exception handler, - the co-processor exception handler will trigger a kernel panic and freeze. - This restriction is introduced to reduce the overhead of saving and restoring - co-processor state (which can be quite large) and in particular remove that - overhead from interrupt handlers. - - The co-processor state save area may be in any convenient per-thread location - such as in the thread control block or above the thread stack area. It need - not be in the interrupt stack frame since interrupts don't use co-processors. - - Along with the save area for each co-processor, two bitmasks with flags per - co-processor (laid out as in the CPENABLE reg) help manage context-switching - co-processors as efficiently as possible: - - XT_CPENABLE - The contents of a non-running thread's CPENABLE register. - It represents the co-processors owned (and whose state is still needed) - by the thread. When a thread is preempted, its CPENABLE is saved here. - When a thread solicits a context-swtich, its CPENABLE is cleared - the - compiler has saved the (caller-saved) co-proc state if it needs to. - When a non-running thread loses ownership of a CP, its bit is cleared. - When a thread runs, it's XT_CPENABLE is loaded into the CPENABLE reg. - Avoids co-processor exceptions when no change of ownership is needed. - - XT_CPSTORED - A bitmask with the same layout as CPENABLE, a bit per co-processor. - Indicates whether the state of each co-processor is saved in the state - save area. When a thread enters the kernel, only the state of co-procs - still enabled in CPENABLE is saved. When the co-processor exception - handler assigns ownership of a co-processor to a thread, it restores - the saved state only if this bit is set, and clears this bit. - - XT_CP_CS_ST - A bitmask with the same layout as CPENABLE, a bit per co-processor. - Indicates whether callee-saved state is saved in the state save area. - Callee-saved state is saved by itself on a solicited context switch, - and restored when needed by the coprocessor exception handler. - Unsolicited switches will cause the entire coprocessor to be saved - when necessary. - - XT_CP_ASA - Pointer to the aligned save area. Allows it to be aligned more than - the overall save area (which might only be stack-aligned or TCB-aligned). - Especially relevant for Xtensa cores configured with a very large data - path that requires alignment greater than 16 bytes (ABI stack alignment). -------------------------------------------------------------------------------- -*/ - -#if XCHAL_CP_NUM > 0 - -/* Offsets of each coprocessor save area within the 'aligned save area': */ -#define XT_CP0_SA 0 -#define XT_CP1_SA ALIGNUP(XCHAL_CP1_SA_ALIGN, XT_CP0_SA + XCHAL_CP0_SA_SIZE) -#define XT_CP2_SA ALIGNUP(XCHAL_CP2_SA_ALIGN, XT_CP1_SA + XCHAL_CP1_SA_SIZE) -#define XT_CP3_SA ALIGNUP(XCHAL_CP3_SA_ALIGN, XT_CP2_SA + XCHAL_CP2_SA_SIZE) -#define XT_CP4_SA ALIGNUP(XCHAL_CP4_SA_ALIGN, XT_CP3_SA + XCHAL_CP3_SA_SIZE) -#define XT_CP5_SA ALIGNUP(XCHAL_CP5_SA_ALIGN, XT_CP4_SA + XCHAL_CP4_SA_SIZE) -#define XT_CP6_SA ALIGNUP(XCHAL_CP6_SA_ALIGN, XT_CP5_SA + XCHAL_CP5_SA_SIZE) -#define XT_CP7_SA ALIGNUP(XCHAL_CP7_SA_ALIGN, XT_CP6_SA + XCHAL_CP6_SA_SIZE) -#define XT_CP_SA_SIZE ALIGNUP(16, XT_CP7_SA + XCHAL_CP7_SA_SIZE) - -/* Offsets within the overall save area: */ -#define XT_CPENABLE 0 /* (2 bytes) coprocessors active for this thread */ -#define XT_CPSTORED 2 /* (2 bytes) coprocessors saved for this thread */ -#define XT_CP_CS_ST 4 /* (2 bytes) coprocessor callee-saved regs stored for this thread */ -#define XT_CP_ASA 8 /* (4 bytes) ptr to aligned save area */ -/* Overall size allows for dynamic alignment: */ -#define XT_CP_SIZE (12 + XT_CP_SA_SIZE + XCHAL_TOTAL_SA_ALIGN) -#else -#define XT_CP_SIZE 0 -#endif - - -/* - Macro to get the current core ID. Only uses the reg given as an argument. - Reading PRID on the ESP32 gives us 0xCDCD on the PRO processor (0) - and 0xABAB on the APP CPU (1). We can distinguish between the two by checking - bit 13: it's 1 on the APP and 0 on the PRO processor. -*/ -#ifdef __ASSEMBLER__ - .macro getcoreid reg - rsr.prid \reg - extui \reg,\reg,13,1 - .endm -#endif - -/* Note: These are different to xCoreID used in ESP-IDF FreeRTOS, most places use - 0 and 1 which are determined by checking bit 13 (see previous comment) -*/ -#define CORE_ID_REGVAL_PRO 0xCDCD -#define CORE_ID_REGVAL_APP 0xABAB - -/* Included for compatibility, recommend using CORE_ID_REGVAL_PRO instead */ -#define CORE_ID_PRO CORE_ID_REGVAL_PRO - -/* Included for compatibility, recommend using CORE_ID_REGVAL_APP instead */ -#define CORE_ID_APP CORE_ID_REGVAL_APP - -/* -------------------------------------------------------------------------------- - MACROS TO HANDLE ABI SPECIFICS OF FUNCTION ENTRY AND RETURN - - Convenient where the frame size requirements are the same for both ABIs. - ENTRY(sz), RET(sz) are for framed functions (have locals or make calls). - ENTRY0, RET0 are for frameless functions (no locals, no calls). - - where size = size of stack frame in bytes (must be >0 and aligned to 16). - For framed functions the frame is created and the return address saved at - base of frame (Call0 ABI) or as determined by hardware (Windowed ABI). - For frameless functions, there is no frame and return address remains in a0. - Note: Because CPP macros expand to a single line, macros requiring multi-line - expansions are implemented as assembler macros. -------------------------------------------------------------------------------- -*/ - -#ifdef __ASSEMBLER__ -#ifdef __XTENSA_CALL0_ABI__ - /* Call0 */ - #define ENTRY(sz) entry1 sz - .macro entry1 size=0x10 - addi sp, sp, -\size - s32i a0, sp, 0 - .endm - #define ENTRY0 - #define RET(sz) ret1 sz - .macro ret1 size=0x10 - l32i a0, sp, 0 - addi sp, sp, \size - ret - .endm - #define RET0 ret -#else - /* Windowed */ - #define ENTRY(sz) entry sp, sz - #define ENTRY0 entry sp, 0x10 - #define RET(sz) retw - #define RET0 retw -#endif -#endif - - - - - -#endif /* XTENSA_CONTEXT_H */ - +/* This header file has been moved, please include in future */ +#include \ No newline at end of file diff --git a/components/freertos/xtensa/portasm.S b/components/freertos/xtensa/portasm.S index b66631fd17..2ec883337c 100644 --- a/components/freertos/xtensa/portasm.S +++ b/components/freertos/xtensa/portasm.S @@ -386,7 +386,8 @@ _frxt_tick_timer_init: call0 xt_ints_on #else movi a6, XT_TIMER_INTEN - call4 xt_ints_on + movi a3, xt_ints_on + callx4 a3 #endif RET(16) diff --git a/components/hal/CMakeLists.txt b/components/hal/CMakeLists.txt index cb365bb3cf..0384e7587b 100644 --- a/components/hal/CMakeLists.txt +++ b/components/hal/CMakeLists.txt @@ -30,7 +30,9 @@ if(NOT BOOTLOADER_BUILD) "spi_flash_hal.c" "spi_flash_hal_iram.c" "soc_hal.c" - "twai_hal.c") + "twai_hal.c" + "interrupt_controller_hal.c" + "${target}/interrupt_descriptor_table.c") if(${target} STREQUAL "esp32") list(APPEND srcs diff --git a/components/hal/esp32/include/hal/interrupt_controller_ll.h b/components/hal/esp32/include/hal/interrupt_controller_ll.h new file mode 100644 index 0000000000..3c3d598149 --- /dev/null +++ b/components/hal/esp32/include/hal/interrupt_controller_ll.h @@ -0,0 +1,105 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include "soc/soc_caps.h" +#include "soc/soc.h" +#include "xtensa/xtensa_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief enable interrupts specified by the mask + * + * @param mask bitmask of interrupts that needs to be enabled + */ +static inline void intr_cntrl_ll_enable_interrupts(uint32_t mask) +{ + xt_ints_on(mask); +} + +/** + * @brief disable interrupts specified by the mask + * + * @param mask bitmask of interrupts that needs to be disabled + */ +static inline void intr_cntrl_ll_disable_interrupts(uint32_t mask) +{ + xt_ints_off(mask); +} + +/** + * @brief checks if given interrupt number has a valid handler + * + * @param intr interrupt number ranged from 0 to 31 + * @param cpu cpu number ranged betweeen 0 to SOC_CPU_CORES_NUM - 1 + * @return true for valid handler, false otherwise + */ +static inline bool intr_cntrl_ll_has_handler(uint8_t intr, uint8_t cpu) +{ + return xt_int_has_handler(intr, cpu); +} + +/** + * @brief sets interrupt handler and optional argument of a given interrupt number + * + * @param intr interrupt number ranged from 0 to 31 + * @param handler handler invoked when an interrupt occurs + * @param arg optional argument to pass to the handler + */ +static inline void intr_cntrl_ll_set_int_handler(uint8_t intr, interrupt_handler_t handler, void * arg) +{ + xt_set_interrupt_handler(intr, (xt_handler)handler, arg); +} + +/** + * @brief Gets argument passed to handler of a given interrupt number + * + * @param intr interrupt number ranged from 0 to 31 + * + * @return argument used by handler of passed interrupt number + */ +static inline void * intr_cntrl_ll_get_int_handler_arg(uint8_t intr) +{ + return xt_get_interrupt_handler_arg(intr); +} + +/** + * @brief Disables interrupts that are not located in iram + * + * @param newmask mask of interrupts needs to be disabled + * @return oldmask where to store old interrupts state + */ +static inline uint32_t intr_cntrl_ll_disable_int_mask(uint32_t newmask) +{ + return xt_int_disable_mask(newmask); +} + +/** + * @brief Enables interrupts that are not located in iram + * + * @param newmask mask of interrupts needs to be disabled + */ +static inline void intr_cntrl_ll_enable_int_mask(uint32_t newmask) +{ + xt_int_enable_mask(newmask); +} + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/hal/esp32/interrupt_descriptor_table.c b/components/hal/esp32/interrupt_descriptor_table.c new file mode 100644 index 0000000000..fefa693795 --- /dev/null +++ b/components/hal/esp32/interrupt_descriptor_table.c @@ -0,0 +1,75 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "sdkconfig.h" +#include "hal/interrupt_controller_hal.h" +#include "hal/interrupt_controller_ll.h" +#include "soc/soc_caps.h" +#include "soc/soc.h" + +//We should mark the interrupt for the timer used by FreeRTOS as reserved. The specific timer +//is selectable using menuconfig; we use these cpp bits to convert that into something we can use in +//the table below. +#if CONFIG_FREERTOS_CORETIMER_0 +#define INT6RES INTDESC_RESVD +#else +#define INT6RES INTDESC_SPECIAL +#endif + +#if CONFIG_FREERTOS_CORETIMER_1 +#define INT15RES INTDESC_RESVD +#else +#define INT15RES INTDESC_SPECIAL +#endif + +//This is basically a software-readable version of the interrupt usage table in include/soc/soc.h +const static int_desc_t interrupt_descriptor_table [32]={ + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //0 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //1 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //2 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //3 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_NORMAL} }, //4 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //5 + { 1, INTTP_NA, {INT6RES, INT6RES } }, //6 + { 1, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL}}, //7 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //8 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //9 + { 1, INTTP_EDGE , {INTDESC_NORMAL, INTDESC_NORMAL} }, //10 + { 3, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL}}, //11 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //12 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //13 + { 7, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //14, NMI + { 3, INTTP_NA, {INT15RES, INT15RES } }, //15 + { 5, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL} }, //16 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //17 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //18 + { 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //19 + { 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //20 + { 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //21 + { 3, INTTP_EDGE, {INTDESC_RESVD, INTDESC_NORMAL} }, //22 + { 3, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //23 + { 4, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_NORMAL} }, //24 + { 4, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //25 + { 5, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_RESVD } }, //26 + { 3, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //27 + { 4, INTTP_EDGE, {INTDESC_NORMAL, INTDESC_NORMAL} }, //28 + { 3, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL}}, //29 + { 4, INTTP_EDGE, {INTDESC_RESVD, INTDESC_RESVD } }, //30 + { 5, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //31 +}; + +const int_desc_t *interrupt_controller_hal_desc_table(void) +{ + return interrupt_descriptor_table; +} \ No newline at end of file diff --git a/components/hal/esp32s2/include/hal/interrupt_controller_ll.h b/components/hal/esp32s2/include/hal/interrupt_controller_ll.h new file mode 100644 index 0000000000..3c3d598149 --- /dev/null +++ b/components/hal/esp32s2/include/hal/interrupt_controller_ll.h @@ -0,0 +1,105 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include "soc/soc_caps.h" +#include "soc/soc.h" +#include "xtensa/xtensa_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief enable interrupts specified by the mask + * + * @param mask bitmask of interrupts that needs to be enabled + */ +static inline void intr_cntrl_ll_enable_interrupts(uint32_t mask) +{ + xt_ints_on(mask); +} + +/** + * @brief disable interrupts specified by the mask + * + * @param mask bitmask of interrupts that needs to be disabled + */ +static inline void intr_cntrl_ll_disable_interrupts(uint32_t mask) +{ + xt_ints_off(mask); +} + +/** + * @brief checks if given interrupt number has a valid handler + * + * @param intr interrupt number ranged from 0 to 31 + * @param cpu cpu number ranged betweeen 0 to SOC_CPU_CORES_NUM - 1 + * @return true for valid handler, false otherwise + */ +static inline bool intr_cntrl_ll_has_handler(uint8_t intr, uint8_t cpu) +{ + return xt_int_has_handler(intr, cpu); +} + +/** + * @brief sets interrupt handler and optional argument of a given interrupt number + * + * @param intr interrupt number ranged from 0 to 31 + * @param handler handler invoked when an interrupt occurs + * @param arg optional argument to pass to the handler + */ +static inline void intr_cntrl_ll_set_int_handler(uint8_t intr, interrupt_handler_t handler, void * arg) +{ + xt_set_interrupt_handler(intr, (xt_handler)handler, arg); +} + +/** + * @brief Gets argument passed to handler of a given interrupt number + * + * @param intr interrupt number ranged from 0 to 31 + * + * @return argument used by handler of passed interrupt number + */ +static inline void * intr_cntrl_ll_get_int_handler_arg(uint8_t intr) +{ + return xt_get_interrupt_handler_arg(intr); +} + +/** + * @brief Disables interrupts that are not located in iram + * + * @param newmask mask of interrupts needs to be disabled + * @return oldmask where to store old interrupts state + */ +static inline uint32_t intr_cntrl_ll_disable_int_mask(uint32_t newmask) +{ + return xt_int_disable_mask(newmask); +} + +/** + * @brief Enables interrupts that are not located in iram + * + * @param newmask mask of interrupts needs to be disabled + */ +static inline void intr_cntrl_ll_enable_int_mask(uint32_t newmask) +{ + xt_int_enable_mask(newmask); +} + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/hal/esp32s2/interrupt_descriptor_table.c b/components/hal/esp32s2/interrupt_descriptor_table.c new file mode 100644 index 0000000000..1d31f2e0d1 --- /dev/null +++ b/components/hal/esp32s2/interrupt_descriptor_table.c @@ -0,0 +1,75 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "sdkconfig.h" +#include "hal/interrupt_controller_hal.h" +#include "hal/interrupt_controller_ll.h" +#include "soc/soc_caps.h" +#include "soc/soc.h" + +//We should mark the interrupt for the timer used by FreeRTOS as reserved. The specific timer +//is selectable using menuconfig; we use these cpp bits to convert that into something we can use in +//the table below. +#if CONFIG_FREERTOS_CORETIMER_0 +#define INT6RES INTDESC_RESVD +#else +#define INT6RES INTDESC_SPECIAL +#endif + +#if CONFIG_FREERTOS_CORETIMER_1 +#define INT15RES INTDESC_RESVD +#else +#define INT15RES INTDESC_SPECIAL +#endif + +//This is basically a software-readable version of the interrupt usage table in include/soc/soc.h +const static int_desc_t interrupt_descriptor_table [32]={ + { 1, INTTP_LEVEL, {INTDESC_RESVD} }, //0 + { 1, INTTP_LEVEL, {INTDESC_RESVD} }, //1 + { 1, INTTP_LEVEL, {INTDESC_NORMAL} }, //2 + { 1, INTTP_LEVEL, {INTDESC_NORMAL} }, //3 + { 1, INTTP_LEVEL, {INTDESC_RESVD} }, //4 + { 1, INTTP_LEVEL, {INTDESC_RESVD} }, //5 + { 1, INTTP_NA, {INT6RES} }, //6 + { 1, INTTP_NA, {INTDESC_SPECIAL}}, //7 + { 1, INTTP_LEVEL, {INTDESC_RESVD } }, //8 + { 1, INTTP_LEVEL, {INTDESC_NORMAL } }, //9 + { 1, INTTP_EDGE , {INTDESC_NORMAL } }, //10 + { 3, INTTP_NA, {INTDESC_SPECIAL }}, //11 + { 1, INTTP_LEVEL, {INTDESC_NORMAL } }, //12 + { 1, INTTP_LEVEL, {INTDESC_NORMAL} }, //13 + { 7, INTTP_LEVEL, {INTDESC_RESVD} }, //14, NMI + { 3, INTTP_NA, {INT15RES} }, //15 + { 5, INTTP_NA, {INTDESC_SPECIAL } }, //16 + { 1, INTTP_LEVEL, {INTDESC_NORMAL } }, //17 + { 1, INTTP_LEVEL, {INTDESC_NORMAL } }, //18 + { 2, INTTP_LEVEL, {INTDESC_NORMAL } }, //19 + { 2, INTTP_LEVEL, {INTDESC_NORMAL } }, //20 + { 2, INTTP_LEVEL, {INTDESC_NORMAL } }, //21 + { 3, INTTP_EDGE, {INTDESC_RESVD } }, //22 + { 3, INTTP_LEVEL, {INTDESC_NORMAL } }, //23 + { 4, INTTP_LEVEL, {INTDESC_RESVD } }, //24 + { 4, INTTP_LEVEL, {INTDESC_RESVD } }, //25 + { 5, INTTP_LEVEL, {INTDESC_NORMAL } }, //26 + { 3, INTTP_LEVEL, {INTDESC_RESVD } }, //27 + { 4, INTTP_EDGE, {INTDESC_NORMAL } }, //28 + { 3, INTTP_NA, {INTDESC_SPECIAL }}, //29 + { 4, INTTP_EDGE, {INTDESC_RESVD } }, //30 + { 5, INTTP_LEVEL, {INTDESC_RESVD } }, //31 +}; + +const int_desc_t *interrupt_controller_hal_desc_table(void) +{ + return interrupt_descriptor_table; +} \ No newline at end of file diff --git a/components/hal/esp32s3/include/hal/interrupt_controller_ll.h b/components/hal/esp32s3/include/hal/interrupt_controller_ll.h new file mode 100644 index 0000000000..3c3d598149 --- /dev/null +++ b/components/hal/esp32s3/include/hal/interrupt_controller_ll.h @@ -0,0 +1,105 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include "soc/soc_caps.h" +#include "soc/soc.h" +#include "xtensa/xtensa_api.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief enable interrupts specified by the mask + * + * @param mask bitmask of interrupts that needs to be enabled + */ +static inline void intr_cntrl_ll_enable_interrupts(uint32_t mask) +{ + xt_ints_on(mask); +} + +/** + * @brief disable interrupts specified by the mask + * + * @param mask bitmask of interrupts that needs to be disabled + */ +static inline void intr_cntrl_ll_disable_interrupts(uint32_t mask) +{ + xt_ints_off(mask); +} + +/** + * @brief checks if given interrupt number has a valid handler + * + * @param intr interrupt number ranged from 0 to 31 + * @param cpu cpu number ranged betweeen 0 to SOC_CPU_CORES_NUM - 1 + * @return true for valid handler, false otherwise + */ +static inline bool intr_cntrl_ll_has_handler(uint8_t intr, uint8_t cpu) +{ + return xt_int_has_handler(intr, cpu); +} + +/** + * @brief sets interrupt handler and optional argument of a given interrupt number + * + * @param intr interrupt number ranged from 0 to 31 + * @param handler handler invoked when an interrupt occurs + * @param arg optional argument to pass to the handler + */ +static inline void intr_cntrl_ll_set_int_handler(uint8_t intr, interrupt_handler_t handler, void * arg) +{ + xt_set_interrupt_handler(intr, (xt_handler)handler, arg); +} + +/** + * @brief Gets argument passed to handler of a given interrupt number + * + * @param intr interrupt number ranged from 0 to 31 + * + * @return argument used by handler of passed interrupt number + */ +static inline void * intr_cntrl_ll_get_int_handler_arg(uint8_t intr) +{ + return xt_get_interrupt_handler_arg(intr); +} + +/** + * @brief Disables interrupts that are not located in iram + * + * @param newmask mask of interrupts needs to be disabled + * @return oldmask where to store old interrupts state + */ +static inline uint32_t intr_cntrl_ll_disable_int_mask(uint32_t newmask) +{ + return xt_int_disable_mask(newmask); +} + +/** + * @brief Enables interrupts that are not located in iram + * + * @param newmask mask of interrupts needs to be disabled + */ +static inline void intr_cntrl_ll_enable_int_mask(uint32_t newmask) +{ + xt_int_enable_mask(newmask); +} + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/hal/esp32s3/interrupt_descriptor_table.c b/components/hal/esp32s3/interrupt_descriptor_table.c new file mode 100644 index 0000000000..fefa693795 --- /dev/null +++ b/components/hal/esp32s3/interrupt_descriptor_table.c @@ -0,0 +1,75 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "sdkconfig.h" +#include "hal/interrupt_controller_hal.h" +#include "hal/interrupt_controller_ll.h" +#include "soc/soc_caps.h" +#include "soc/soc.h" + +//We should mark the interrupt for the timer used by FreeRTOS as reserved. The specific timer +//is selectable using menuconfig; we use these cpp bits to convert that into something we can use in +//the table below. +#if CONFIG_FREERTOS_CORETIMER_0 +#define INT6RES INTDESC_RESVD +#else +#define INT6RES INTDESC_SPECIAL +#endif + +#if CONFIG_FREERTOS_CORETIMER_1 +#define INT15RES INTDESC_RESVD +#else +#define INT15RES INTDESC_SPECIAL +#endif + +//This is basically a software-readable version of the interrupt usage table in include/soc/soc.h +const static int_desc_t interrupt_descriptor_table [32]={ + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //0 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //1 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //2 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //3 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_NORMAL} }, //4 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //5 + { 1, INTTP_NA, {INT6RES, INT6RES } }, //6 + { 1, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL}}, //7 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //8 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //9 + { 1, INTTP_EDGE , {INTDESC_NORMAL, INTDESC_NORMAL} }, //10 + { 3, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL}}, //11 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //12 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //13 + { 7, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //14, NMI + { 3, INTTP_NA, {INT15RES, INT15RES } }, //15 + { 5, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL} }, //16 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //17 + { 1, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //18 + { 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //19 + { 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //20 + { 2, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //21 + { 3, INTTP_EDGE, {INTDESC_RESVD, INTDESC_NORMAL} }, //22 + { 3, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_NORMAL} }, //23 + { 4, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_NORMAL} }, //24 + { 4, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //25 + { 5, INTTP_LEVEL, {INTDESC_NORMAL, INTDESC_RESVD } }, //26 + { 3, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //27 + { 4, INTTP_EDGE, {INTDESC_NORMAL, INTDESC_NORMAL} }, //28 + { 3, INTTP_NA, {INTDESC_SPECIAL,INTDESC_SPECIAL}}, //29 + { 4, INTTP_EDGE, {INTDESC_RESVD, INTDESC_RESVD } }, //30 + { 5, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //31 +}; + +const int_desc_t *interrupt_controller_hal_desc_table(void) +{ + return interrupt_descriptor_table; +} \ No newline at end of file diff --git a/components/hal/include/hal/interrupt_controller_hal.h b/components/hal/include/hal/interrupt_controller_hal.h new file mode 100644 index 0000000000..ae230c8e1f --- /dev/null +++ b/components/hal/include/hal/interrupt_controller_hal.h @@ -0,0 +1,170 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include +#include "hal/interrupt_controller_types.h" +#include "hal/interrupt_controller_ll.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Gets target platform interrupt descriptor table + * + * @return Address of interrupt descriptor table + */ +__attribute__((pure)) const int_desc_t *interrupt_controller_hal_desc_table(void); + +/** + * @brief Gets the interrupt type given an interrupt number. + * + * @param interrupt_number Interrupt number 0 to 31 + * @return interrupt type + */ +__attribute__((pure)) int_type_t interrupt_controller_hal_desc_type(int interrupt_number); + +/** + * @brief Gets the interrupt level given an interrupt number. + * + * @param interrupt_number Interrupt number 0 to 31 + * @return interrupt level bitmask + */ +__attribute__((pure)) int interrupt_controller_hal_desc_level(int interrupt_number); + +/** + * @brief Gets the cpu flags given the interrupt number and target cpu. + * + * @param interrupt_number Interrupt number 0 to 31 + * @param cpu_number CPU number between 0 and SOC_CPU_CORES_NUM - 1 + * @return flags for that interrupt number + */ +__attribute__((pure)) uint32_t interrupt_controller_hal_desc_flags(int interrupt_number, int cpu_number); + +/** + * @brief Gets the interrupt type given an interrupt number. + * + * @param interrupt_number Interrupt number 0 to 31 + * @return interrupt type + */ +static inline int_type_t interrupt_controller_hal_get_type(int interrupt_number) +{ + return interrupt_controller_hal_desc_type(interrupt_number); +} + +/** + * @brief Gets the interrupt level given an interrupt number. + * + * @param interrupt_number Interrupt number 0 to 31 + * @return interrupt level bitmask + */ +static inline int interrupt_controller_hal_get_level(int interrupt_number) +{ + return interrupt_controller_hal_desc_level(interrupt_number); +} + +/** + * @brief Gets the cpu flags given the interrupt number and target cpu. + * + * @param interrupt_number Interrupt number 0 to 31 + * @param cpu_number CPU number between 0 and SOC_CPU_CORES_NUM - 1 + * @return flags for that interrupt number + */ +static inline uint32_t interrupt_controller_hal_get_cpu_desc_flags(int interrupt_number, int cpu_number) +{ + return interrupt_controller_hal_desc_flags(interrupt_number, cpu_number); +} + +/** + * @brief enable interrupts specified by the mask + * + * @param mask bitmask of interrupts that needs to be enabled + */ +static inline void interrupt_controller_hal_enable_interrupts(uint32_t mask) +{ + intr_cntrl_ll_enable_interrupts(mask); +} + +/** + * @brief disable interrupts specified by the mask + * + * @param mask bitmask of interrupts that needs to be disabled + */ +static inline void interrupt_controller_hal_disable_interrupts(uint32_t mask) +{ + intr_cntrl_ll_disable_interrupts(mask); +} + +/** + * @brief checks if given interrupt number has a valid handler + * + * @param intr interrupt number ranged from 0 to 31 + * @param cpu cpu number ranged betweeen 0 to SOC_CPU_CORES_NUM - 1 + * @return true for valid handler, false otherwise + */ +static inline bool interrupt_controller_hal_has_handler(int intr, int cpu) +{ + return intr_cntrl_ll_has_handler(intr, cpu); +} + +/** + * @brief sets interrupt handler and optional argument of a given interrupt number + * + * @param intr interrupt number ranged from 0 to 31 + * @param handler handler invoked when an interrupt occurs + * @param arg optional argument to pass to the handler + */ +static inline void interrupt_controller_hal_set_int_handler(uint8_t intr, interrupt_handler_t handler, void *arg) +{ + intr_cntrl_ll_set_int_handler(intr, handler, arg); +} + +/** + * @brief Gets argument passed to handler of a given interrupt number + * + * @param intr interrupt number ranged from 0 to 31 + * + * @return argument used by handler of passed interrupt number + */ +static inline void * interrupt_controller_hal_get_int_handler_arg(uint8_t intr) +{ + return intr_cntrl_ll_get_int_handler_arg(intr); +} + +/** + * @brief Disables interrupts that are not located in iram + * + * @param newmask mask of interrupts needs to be disabled + * @return oldmask where to store old interrupts state + */ +static inline uint32_t interrupt_controller_hal_disable_int_mask(uint32_t newmask) +{ + return intr_cntrl_ll_disable_int_mask(newmask); +} + +/** + * @brief Enables interrupts that are not located in iram + * + * @param newmask mask of interrupts needs to be disabled + */ +static inline void interrupt_controller_hal_enable_int_mask(uint32_t newmask) +{ + intr_cntrl_ll_enable_int_mask(newmask); +} + +#ifdef __cplusplus +} +#endif diff --git a/components/hal/include/hal/interrupt_controller_types.h b/components/hal/include/hal/interrupt_controller_types.h new file mode 100644 index 0000000000..639317f47e --- /dev/null +++ b/components/hal/include/hal/interrupt_controller_types.h @@ -0,0 +1,46 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#pragma once + +#include "soc/soc_caps.h" +#include "soc/soc.h" + +#ifdef __cplusplus +extern "C" { +#endif + +typedef enum { + INTDESC_NORMAL=0, + INTDESC_RESVD, + INTDESC_SPECIAL +} int_desc_flag_t; + +typedef enum { + INTTP_LEVEL=0, + INTTP_EDGE, + INTTP_NA +} int_type_t; + +typedef struct { + int level; + int_type_t type; + int_desc_flag_t cpuflags[SOC_CPU_CORES_NUM]; +} int_desc_t; + +typedef void (*interrupt_handler_t)(void *arg); + +#ifdef __cplusplus +} +#endif \ No newline at end of file diff --git a/components/hal/interrupt_controller_hal.c b/components/hal/interrupt_controller_hal.c new file mode 100644 index 0000000000..557aadfa75 --- /dev/null +++ b/components/hal/interrupt_controller_hal.c @@ -0,0 +1,33 @@ +// Copyright 2020 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#include "hal/interrupt_controller_hal.h" + +int_type_t interrupt_controller_hal_desc_type(int interrupt_number) +{ + const int_desc_t *int_desc = interrupt_controller_hal_desc_table(); + return(int_desc[interrupt_number].type); +} + +int interrupt_controller_hal_desc_level(int interrupt_number) +{ + const int_desc_t *int_desc = interrupt_controller_hal_desc_table(); + return(int_desc[interrupt_number].level); +} + +int_desc_flag_t interrupt_controller_hal_desc_flags(int interrupt_number, int cpu_number) +{ + const int_desc_t *int_desc = interrupt_controller_hal_desc_table(); + return(int_desc[interrupt_number].cpuflags[cpu_number]); +} \ No newline at end of file diff --git a/components/xtensa/CMakeLists.txt b/components/xtensa/CMakeLists.txt index 41d97a6871..8e59467673 100644 --- a/components/xtensa/CMakeLists.txt +++ b/components/xtensa/CMakeLists.txt @@ -11,6 +11,8 @@ else() "expression_with_stack_xtensa_asm.S" "eri.c" "trax.c" + "xtensa_intr.c" + "xtensa_intr_asm.S" "${target}/trax_init.c" ) diff --git a/components/xtensa/include/xtensa/xtensa_api.h b/components/xtensa/include/xtensa/xtensa_api.h new file mode 100644 index 0000000000..7f263b8393 --- /dev/null +++ b/components/xtensa/include/xtensa/xtensa_api.h @@ -0,0 +1,181 @@ +/******************************************************************************* +Copyright (c) 2006-2015 Cadence Design Systems Inc. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +******************************************************************************/ + +/****************************************************************************** + Xtensa-specific API for RTOS ports. +******************************************************************************/ + +#ifndef __XTENSA_API_H__ +#define __XTENSA_API_H__ + +#include +#include +#include "xtensa_context.h" + + +/* Typedef for C-callable interrupt handler function */ +typedef void (*xt_handler)(void *); + +/* Typedef for C-callable exception handler function */ +typedef void (*xt_exc_handler)(XtExcFrame *); + + +/* +------------------------------------------------------------------------------- + Call this function to set a handler for the specified exception. The handler + will be installed on the core that calls this function. + + n - Exception number (type) + f - Handler function address, NULL to uninstall handler. + + The handler will be passed a pointer to the exception frame, which is created + on the stack of the thread that caused the exception. + + If the handler returns, the thread context will be restored and the faulting + instruction will be retried. Any values in the exception frame that are + modified by the handler will be restored as part of the context. For details + of the exception frame structure see xtensa_context.h. +------------------------------------------------------------------------------- +*/ +extern xt_exc_handler xt_set_exception_handler(int n, xt_exc_handler f); + + +/* +------------------------------------------------------------------------------- + Call this function to set a handler for the specified interrupt. The handler + will be installed on the core that calls this function. + + n - Interrupt number. + f - Handler function address, NULL to uninstall handler. + arg - Argument to be passed to handler. +------------------------------------------------------------------------------- +*/ +extern xt_handler xt_set_interrupt_handler(int n, xt_handler f, void * arg); + + +/* +------------------------------------------------------------------------------- + Call this function to enable the specified interrupts on the core that runs + this code. + + mask - Bit mask of interrupts to be enabled. +------------------------------------------------------------------------------- +*/ +extern void xt_ints_on(unsigned int mask); + + +/* +------------------------------------------------------------------------------- + Call this function to disable the specified interrupts on the core that runs + this code. + + mask - Bit mask of interrupts to be disabled. +------------------------------------------------------------------------------- +*/ +extern void xt_ints_off(unsigned int mask); + + +/* +------------------------------------------------------------------------------- + Call this function to set the specified (s/w) interrupt. +------------------------------------------------------------------------------- +*/ +static inline void xt_set_intset(unsigned int arg) +{ + xthal_set_intset(arg); +} + + +/* +------------------------------------------------------------------------------- + Call this function to clear the specified (s/w or edge-triggered) + interrupt. +------------------------------------------------------------------------------- +*/ +static inline void xt_set_intclear(unsigned int arg) +{ + xthal_set_intclear(arg); +} + +/* +------------------------------------------------------------------------------- + Call this function to get handler's argument for the specified interrupt. + + n - Interrupt number. +------------------------------------------------------------------------------- +*/ +extern void * xt_get_interrupt_handler_arg(int n); + +/* +------------------------------------------------------------------------------- + Call this function to check if the specified interrupt is free to use. + + intr - Interrupt number. + cpu - cpu number. +------------------------------------------------------------------------------- +*/ +bool xt_int_has_handler(int intr, int cpu); + +/* +------------------------------------------------------------------------------- + Call this function to disable non iram located interrupts. + + newmask - mask containing the interrupts to disable. +------------------------------------------------------------------------------- +*/ +static inline uint32_t xt_int_disable_mask(uint32_t newmask) +{ + uint32_t oldint; + asm volatile ( + "movi %0,0\n" + "xsr %0,INTENABLE\n" //disable all ints first + "rsync\n" + "and a3,%0,%1\n" //mask ints that need disabling + "wsr a3,INTENABLE\n" //write back + "rsync\n" + :"=&r"(oldint):"r"(newmask):"a3"); + + return oldint; +} + +/* +------------------------------------------------------------------------------- + Call this function to enable non iram located interrupts. + + newmask - mask containing the interrupts to enable. +------------------------------------------------------------------------------- +*/ +static inline void xt_int_enable_mask(uint32_t newmask) +{ + asm volatile ( + "movi a3,0\n" + "xsr a3,INTENABLE\n" + "rsync\n" + "or a3,a3,%0\n" + "wsr a3,INTENABLE\n" + "rsync\n" + ::"r"(newmask):"a3"); +} + +#endif /* __XTENSA_API_H__ */ + diff --git a/components/xtensa/include/xtensa/xtensa_context.h b/components/xtensa/include/xtensa/xtensa_context.h new file mode 100644 index 0000000000..120676dad4 --- /dev/null +++ b/components/xtensa/include/xtensa/xtensa_context.h @@ -0,0 +1,387 @@ +/******************************************************************************* +Copyright (c) 2006-2015 Cadence Design Systems Inc. + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +-------------------------------------------------------------------------------- + + XTENSA CONTEXT FRAMES AND MACROS FOR RTOS ASSEMBLER SOURCES + +This header contains definitions and macros for use primarily by Xtensa +RTOS assembly coded source files. It includes and uses the Xtensa hardware +abstraction layer (HAL) to deal with config specifics. It may also be +included in C source files. + +!! Supports only Xtensa Exception Architecture 2 (XEA2). XEA1 not supported. !! + +NOTE: The Xtensa architecture requires stack pointer alignment to 16 bytes. + +*******************************************************************************/ + +#ifndef XTENSA_CONTEXT_H +#define XTENSA_CONTEXT_H + +#ifdef __ASSEMBLER__ +#include +#endif + +#include +#include +#include +#include + + +/* Align a value up to nearest n-byte boundary, where n is a power of 2. */ +#define ALIGNUP(n, val) (((val) + (n)-1) & -(n)) + + +/* +------------------------------------------------------------------------------- + Macros that help define structures for both C and assembler. +------------------------------------------------------------------------------- +*/ + +#ifdef STRUCT_BEGIN +#undef STRUCT_BEGIN +#undef STRUCT_FIELD +#undef STRUCT_AFIELD +#undef STRUCT_END +#endif + +#if defined(_ASMLANGUAGE) || defined(__ASSEMBLER__) + +#define STRUCT_BEGIN .pushsection .text; .struct 0 +#define STRUCT_FIELD(ctype,size,asname,name) asname: .space size +#define STRUCT_AFIELD(ctype,size,asname,name,n) asname: .space (size)*(n) +#define STRUCT_END(sname) sname##Size:; .popsection + +#else + +#define STRUCT_BEGIN typedef struct { +#define STRUCT_FIELD(ctype,size,asname,name) ctype name; +#define STRUCT_AFIELD(ctype,size,asname,name,n) ctype name[n]; +#define STRUCT_END(sname) } sname; + +#endif //_ASMLANGUAGE || __ASSEMBLER__ + + +/* +------------------------------------------------------------------------------- + INTERRUPT/EXCEPTION STACK FRAME FOR A THREAD OR NESTED INTERRUPT + + A stack frame of this structure is allocated for any interrupt or exception. + It goes on the current stack. If the RTOS has a system stack for handling + interrupts, every thread stack must allow space for just one interrupt stack + frame, then nested interrupt stack frames go on the system stack. + + The frame includes basic registers (explicit) and "extra" registers introduced + by user TIE or the use of the MAC16 option in the user's Xtensa config. + The frame size is minimized by omitting regs not applicable to user's config. + + For Windowed ABI, this stack frame includes the interruptee's base save area, + another base save area to manage gcc nested functions, and a little temporary + space to help manage the spilling of the register windows. +------------------------------------------------------------------------------- +*/ + +STRUCT_BEGIN +STRUCT_FIELD (long, 4, XT_STK_EXIT, exit) /* exit point for dispatch */ +STRUCT_FIELD (long, 4, XT_STK_PC, pc) /* return PC */ +STRUCT_FIELD (long, 4, XT_STK_PS, ps) /* return PS */ +STRUCT_FIELD (long, 4, XT_STK_A0, a0) +STRUCT_FIELD (long, 4, XT_STK_A1, a1) /* stack pointer before interrupt */ +STRUCT_FIELD (long, 4, XT_STK_A2, a2) +STRUCT_FIELD (long, 4, XT_STK_A3, a3) +STRUCT_FIELD (long, 4, XT_STK_A4, a4) +STRUCT_FIELD (long, 4, XT_STK_A5, a5) +STRUCT_FIELD (long, 4, XT_STK_A6, a6) +STRUCT_FIELD (long, 4, XT_STK_A7, a7) +STRUCT_FIELD (long, 4, XT_STK_A8, a8) +STRUCT_FIELD (long, 4, XT_STK_A9, a9) +STRUCT_FIELD (long, 4, XT_STK_A10, a10) +STRUCT_FIELD (long, 4, XT_STK_A11, a11) +STRUCT_FIELD (long, 4, XT_STK_A12, a12) +STRUCT_FIELD (long, 4, XT_STK_A13, a13) +STRUCT_FIELD (long, 4, XT_STK_A14, a14) +STRUCT_FIELD (long, 4, XT_STK_A15, a15) +STRUCT_FIELD (long, 4, XT_STK_SAR, sar) +STRUCT_FIELD (long, 4, XT_STK_EXCCAUSE, exccause) +STRUCT_FIELD (long, 4, XT_STK_EXCVADDR, excvaddr) +#if XCHAL_HAVE_LOOPS +STRUCT_FIELD (long, 4, XT_STK_LBEG, lbeg) +STRUCT_FIELD (long, 4, XT_STK_LEND, lend) +STRUCT_FIELD (long, 4, XT_STK_LCOUNT, lcount) +#endif +#ifndef __XTENSA_CALL0_ABI__ +/* Temporary space for saving stuff during window spill */ +STRUCT_FIELD (long, 4, XT_STK_TMP0, tmp0) +STRUCT_FIELD (long, 4, XT_STK_TMP1, tmp1) +STRUCT_FIELD (long, 4, XT_STK_TMP2, tmp2) +#endif +#ifdef XT_USE_SWPRI +/* Storage for virtual priority mask */ +STRUCT_FIELD (long, 4, XT_STK_VPRI, vpri) +#endif +#ifdef XT_USE_OVLY +/* Storage for overlay state */ +STRUCT_FIELD (long, 4, XT_STK_OVLY, ovly) +#endif +STRUCT_END(XtExcFrame) + +#if defined(_ASMLANGUAGE) || defined(__ASSEMBLER__) +#define XT_STK_NEXT1 XtExcFrameSize +#else +#define XT_STK_NEXT1 sizeof(XtExcFrame) +#endif + +/* Allocate extra storage if needed */ +#if XCHAL_EXTRA_SA_SIZE != 0 + +#if XCHAL_EXTRA_SA_ALIGN <= 16 +#define XT_STK_EXTRA ALIGNUP(XCHAL_EXTRA_SA_ALIGN, XT_STK_NEXT1) +#else +/* If need more alignment than stack, add space for dynamic alignment */ +#define XT_STK_EXTRA (ALIGNUP(XCHAL_EXTRA_SA_ALIGN, XT_STK_NEXT1) + XCHAL_EXTRA_SA_ALIGN) +#endif +#define XT_STK_NEXT2 (XT_STK_EXTRA + XCHAL_EXTRA_SA_SIZE) + +#else + +#define XT_STK_NEXT2 XT_STK_NEXT1 + +#endif + +/* +------------------------------------------------------------------------------- + This is the frame size. Add space for 4 registers (interruptee's base save + area) and some space for gcc nested functions if any. +------------------------------------------------------------------------------- +*/ +#define XT_STK_FRMSZ (ALIGNUP(0x10, XT_STK_NEXT2) + 0x20) + + +/* +------------------------------------------------------------------------------- + SOLICITED STACK FRAME FOR A THREAD + + A stack frame of this structure is allocated whenever a thread enters the + RTOS kernel intentionally (and synchronously) to submit to thread scheduling. + It goes on the current thread's stack. + + The solicited frame only includes registers that are required to be preserved + by the callee according to the compiler's ABI conventions, some space to save + the return address for returning to the caller, and the caller's PS register. + + For Windowed ABI, this stack frame includes the caller's base save area. + + Note on XT_SOL_EXIT field: + It is necessary to distinguish a solicited from an interrupt stack frame. + This field corresponds to XT_STK_EXIT in the interrupt stack frame and is + always at the same offset (0). It can be written with a code (usually 0) + to distinguish a solicted frame from an interrupt frame. An RTOS port may + opt to ignore this field if it has another way of distinguishing frames. +------------------------------------------------------------------------------- +*/ + +STRUCT_BEGIN +#ifdef __XTENSA_CALL0_ABI__ +STRUCT_FIELD (long, 4, XT_SOL_EXIT, exit) +STRUCT_FIELD (long, 4, XT_SOL_PC, pc) +STRUCT_FIELD (long, 4, XT_SOL_PS, ps) +STRUCT_FIELD (long, 4, XT_SOL_NEXT, next) +STRUCT_FIELD (long, 4, XT_SOL_A12, a12) /* should be on 16-byte alignment */ +STRUCT_FIELD (long, 4, XT_SOL_A13, a13) +STRUCT_FIELD (long, 4, XT_SOL_A14, a14) +STRUCT_FIELD (long, 4, XT_SOL_A15, a15) +#else +STRUCT_FIELD (long, 4, XT_SOL_EXIT, exit) +STRUCT_FIELD (long, 4, XT_SOL_PC, pc) +STRUCT_FIELD (long, 4, XT_SOL_PS, ps) +STRUCT_FIELD (long, 4, XT_SOL_NEXT, next) +STRUCT_FIELD (long, 4, XT_SOL_A0, a0) /* should be on 16-byte alignment */ +STRUCT_FIELD (long, 4, XT_SOL_A1, a1) +STRUCT_FIELD (long, 4, XT_SOL_A2, a2) +STRUCT_FIELD (long, 4, XT_SOL_A3, a3) +#endif +STRUCT_END(XtSolFrame) + +/* Size of solicited stack frame */ +#define XT_SOL_FRMSZ ALIGNUP(0x10, XtSolFrameSize) + + +/* +------------------------------------------------------------------------------- + CO-PROCESSOR STATE SAVE AREA FOR A THREAD + + The RTOS must provide an area per thread to save the state of co-processors + when that thread does not have control. Co-processors are context-switched + lazily (on demand) only when a new thread uses a co-processor instruction, + otherwise a thread retains ownership of the co-processor even when it loses + control of the processor. An Xtensa co-processor exception is triggered when + any co-processor instruction is executed by a thread that is not the owner, + and the context switch of that co-processor is then peformed by the handler. + Ownership represents which thread's state is currently in the co-processor. + + Co-processors may not be used by interrupt or exception handlers. If an + co-processor instruction is executed by an interrupt or exception handler, + the co-processor exception handler will trigger a kernel panic and freeze. + This restriction is introduced to reduce the overhead of saving and restoring + co-processor state (which can be quite large) and in particular remove that + overhead from interrupt handlers. + + The co-processor state save area may be in any convenient per-thread location + such as in the thread control block or above the thread stack area. It need + not be in the interrupt stack frame since interrupts don't use co-processors. + + Along with the save area for each co-processor, two bitmasks with flags per + co-processor (laid out as in the CPENABLE reg) help manage context-switching + co-processors as efficiently as possible: + + XT_CPENABLE + The contents of a non-running thread's CPENABLE register. + It represents the co-processors owned (and whose state is still needed) + by the thread. When a thread is preempted, its CPENABLE is saved here. + When a thread solicits a context-swtich, its CPENABLE is cleared - the + compiler has saved the (caller-saved) co-proc state if it needs to. + When a non-running thread loses ownership of a CP, its bit is cleared. + When a thread runs, it's XT_CPENABLE is loaded into the CPENABLE reg. + Avoids co-processor exceptions when no change of ownership is needed. + + XT_CPSTORED + A bitmask with the same layout as CPENABLE, a bit per co-processor. + Indicates whether the state of each co-processor is saved in the state + save area. When a thread enters the kernel, only the state of co-procs + still enabled in CPENABLE is saved. When the co-processor exception + handler assigns ownership of a co-processor to a thread, it restores + the saved state only if this bit is set, and clears this bit. + + XT_CP_CS_ST + A bitmask with the same layout as CPENABLE, a bit per co-processor. + Indicates whether callee-saved state is saved in the state save area. + Callee-saved state is saved by itself on a solicited context switch, + and restored when needed by the coprocessor exception handler. + Unsolicited switches will cause the entire coprocessor to be saved + when necessary. + + XT_CP_ASA + Pointer to the aligned save area. Allows it to be aligned more than + the overall save area (which might only be stack-aligned or TCB-aligned). + Especially relevant for Xtensa cores configured with a very large data + path that requires alignment greater than 16 bytes (ABI stack alignment). +------------------------------------------------------------------------------- +*/ + +#if XCHAL_CP_NUM > 0 + +/* Offsets of each coprocessor save area within the 'aligned save area': */ +#define XT_CP0_SA 0 +#define XT_CP1_SA ALIGNUP(XCHAL_CP1_SA_ALIGN, XT_CP0_SA + XCHAL_CP0_SA_SIZE) +#define XT_CP2_SA ALIGNUP(XCHAL_CP2_SA_ALIGN, XT_CP1_SA + XCHAL_CP1_SA_SIZE) +#define XT_CP3_SA ALIGNUP(XCHAL_CP3_SA_ALIGN, XT_CP2_SA + XCHAL_CP2_SA_SIZE) +#define XT_CP4_SA ALIGNUP(XCHAL_CP4_SA_ALIGN, XT_CP3_SA + XCHAL_CP3_SA_SIZE) +#define XT_CP5_SA ALIGNUP(XCHAL_CP5_SA_ALIGN, XT_CP4_SA + XCHAL_CP4_SA_SIZE) +#define XT_CP6_SA ALIGNUP(XCHAL_CP6_SA_ALIGN, XT_CP5_SA + XCHAL_CP5_SA_SIZE) +#define XT_CP7_SA ALIGNUP(XCHAL_CP7_SA_ALIGN, XT_CP6_SA + XCHAL_CP6_SA_SIZE) +#define XT_CP_SA_SIZE ALIGNUP(16, XT_CP7_SA + XCHAL_CP7_SA_SIZE) + +/* Offsets within the overall save area: */ +#define XT_CPENABLE 0 /* (2 bytes) coprocessors active for this thread */ +#define XT_CPSTORED 2 /* (2 bytes) coprocessors saved for this thread */ +#define XT_CP_CS_ST 4 /* (2 bytes) coprocessor callee-saved regs stored for this thread */ +#define XT_CP_ASA 8 /* (4 bytes) ptr to aligned save area */ +/* Overall size allows for dynamic alignment: */ +#define XT_CP_SIZE (12 + XT_CP_SA_SIZE + XCHAL_TOTAL_SA_ALIGN) +#else +#define XT_CP_SIZE 0 +#endif + + +/* + Macro to get the current core ID. Only uses the reg given as an argument. + Reading PRID on the ESP32 gives us 0xCDCD on the PRO processor (0) + and 0xABAB on the APP CPU (1). We can distinguish between the two by checking + bit 13: it's 1 on the APP and 0 on the PRO processor. +*/ +#ifdef __ASSEMBLER__ + .macro getcoreid reg + rsr.prid \reg + extui \reg,\reg,13,1 + .endm +#endif + +/* Note: These are different to xCoreID used in ESP-IDF FreeRTOS, most places use + 0 and 1 which are determined by checking bit 13 (see previous comment) +*/ +#define CORE_ID_REGVAL_PRO 0xCDCD +#define CORE_ID_REGVAL_APP 0xABAB + +/* Included for compatibility, recommend using CORE_ID_REGVAL_PRO instead */ +#define CORE_ID_PRO CORE_ID_REGVAL_PRO + +/* Included for compatibility, recommend using CORE_ID_REGVAL_APP instead */ +#define CORE_ID_APP CORE_ID_REGVAL_APP + +/* +------------------------------------------------------------------------------- + MACROS TO HANDLE ABI SPECIFICS OF FUNCTION ENTRY AND RETURN + + Convenient where the frame size requirements are the same for both ABIs. + ENTRY(sz), RET(sz) are for framed functions (have locals or make calls). + ENTRY0, RET0 are for frameless functions (no locals, no calls). + + where size = size of stack frame in bytes (must be >0 and aligned to 16). + For framed functions the frame is created and the return address saved at + base of frame (Call0 ABI) or as determined by hardware (Windowed ABI). + For frameless functions, there is no frame and return address remains in a0. + Note: Because CPP macros expand to a single line, macros requiring multi-line + expansions are implemented as assembler macros. +------------------------------------------------------------------------------- +*/ + +#ifdef __ASSEMBLER__ +#ifdef __XTENSA_CALL0_ABI__ + /* Call0 */ + #define ENTRY(sz) entry1 sz + .macro entry1 size=0x10 + addi sp, sp, -\size + s32i a0, sp, 0 + .endm + #define ENTRY0 + #define RET(sz) ret1 sz + .macro ret1 size=0x10 + l32i a0, sp, 0 + addi sp, sp, \size + ret + .endm + #define RET0 ret +#else + /* Windowed */ + #define ENTRY(sz) entry sp, sz + #define ENTRY0 entry sp, 0x10 + #define RET(sz) retw + #define RET0 retw +#endif +#endif + + + + + +#endif /* XTENSA_CONTEXT_H */ + diff --git a/components/freertos/xtensa/xtensa_intr.c b/components/xtensa/xtensa_intr.c similarity index 93% rename from components/freertos/xtensa/xtensa_intr.c rename to components/xtensa/xtensa_intr.c index dd6d0744e0..1afd33de6e 100644 --- a/components/freertos/xtensa/xtensa_intr.c +++ b/components/xtensa/xtensa_intr.c @@ -31,8 +31,8 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include "freertos/FreeRTOS.h" -#include "freertos/xtensa_api.h" #include "freertos/portable.h" +#include "xtensa/xtensa_api.h" #include "sdkconfig.h" #include "esp_rom_sys.h" @@ -40,7 +40,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /* Handler table is in xtensa_intr_asm.S */ -extern xt_exc_handler _xt_exception_table[XCHAL_EXCCAUSE_NUM*portNUM_PROCESSORS]; +extern xt_exc_handler _xt_exception_table[]; /* @@ -103,6 +103,11 @@ void xt_unhandled_interrupt(void * arg) esp_rom_printf("Unhandled interrupt %d on cpu %d!\n", (int)arg, xPortGetCoreID()); } +//Returns true if handler for interrupt is not the default unhandled interrupt handler +bool xt_int_has_handler(int intr, int cpu) +{ + return (_xt_interrupt_table[intr*portNUM_PROCESSORS+cpu].handler != xt_unhandled_interrupt); +} /* This function registers a handler for the specified interrupt. The "arg" diff --git a/components/freertos/xtensa/xtensa_intr_asm.S b/components/xtensa/xtensa_intr_asm.S similarity index 98% rename from components/freertos/xtensa/xtensa_intr_asm.S rename to components/xtensa/xtensa_intr_asm.S index f2d236108d..8d1fe72209 100644 --- a/components/freertos/xtensa/xtensa_intr_asm.S +++ b/components/xtensa/xtensa_intr_asm.S @@ -29,8 +29,8 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -#include "xtensa_context.h" -#include "FreeRTOSConfig.h" +#include "xtensa/xtensa_context.h" +#include "freertos/FreeRTOSConfig.h" #if XCHAL_HAVE_INTERRUPTS diff --git a/docs/Doxyfile b/docs/Doxyfile index c132026c47..c1d78ba1fc 100644 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -237,7 +237,7 @@ INPUT = \ ## Himem $(IDF_PATH)/components/esp32/include/esp32/himem.h \ ## Interrupt Allocation - $(IDF_PATH)/components/$(IDF_TARGET)/include/esp_intr_alloc.h \ + $(IDF_PATH)/components/esp_system/include/esp_intr_alloc.h \ ## Watchdogs ## NOTE: for two lines below header_file.inc is not used $(IDF_PATH)/components/esp_common/include/esp_int_wdt.h \ diff --git a/docs/en/api-reference/peripherals/i2c.rst b/docs/en/api-reference/peripherals/i2c.rst index b8a3069749..714fce7812 100644 --- a/docs/en/api-reference/peripherals/i2c.rst +++ b/docs/en/api-reference/peripherals/i2c.rst @@ -75,7 +75,7 @@ After the I2C driver is configured, install it by calling the function :cpp:func - Port number, one of the two port numbers from :cpp:type:`i2c_port_t` - Master or slave, selected from :cpp:type:`i2c_mode_t` - (Slave only) Size of buffers to allocate for sending and receiving data. As I2C is a master-centric bus, data can only go from the slave to the master at the master's request. Therefore, the slave will usually have a send buffer where the slave application writes data. The data remains in the send buffer to be read by the master at the master's own discretion. -- Flags for allocating the interrupt (see ESP_INTR_FLAG_* values in :component_file:`esp32/include/esp_intr_alloc.h`) +- Flags for allocating the interrupt (see ESP_INTR_FLAG_* values in :component_file:`esp_system/include/esp_intr_alloc.h`) .. _i2c-api-master-mode: diff --git a/docs/zh_CN/api-reference/peripherals/i2c.rst b/docs/zh_CN/api-reference/peripherals/i2c.rst index 32de5d3e0a..ef8e738cf4 100644 --- a/docs/zh_CN/api-reference/peripherals/i2c.rst +++ b/docs/zh_CN/api-reference/peripherals/i2c.rst @@ -75,7 +75,7 @@ I2C 驱动程序管理在 I2C 总线上设备的通信,该驱动程序具备 - 端口号,从 :cpp:type:`i2c_port_t` 中二选一 - 主机或从机模式,从 :cpp:type:`i2c_mode_t` 中选择 - (仅限从机模式)分配用于在从机模式下发送和接收数据的缓存区大小。I2C 是一个以主机为中心的总线,数据只能根据主机的请求从从机传输到主机。因此,从机通常有一个发送缓存区,供从应用程序写入数据使用。数据保留在发送缓存区中,由主机自行读取。 -- 用于分配中断的标志(请参考 ESP_INTR_FLAG_* values in :component_file:`esp32/include/esp_intr_alloc.h`) +- 用于分配中断的标志(请参考 ESP_INTR_FLAG_* values in :component_file:`esp_system/include/esp_intr_alloc.h`) .. _i2c-api-master-mode: