mirror of
https://github.com/espressif/esp-idf.git
synced 2024-09-20 00:36:01 -04:00
Merge branch 'feature/intr_alloc' into 'master'
feature/intr_alloc: moved interrupt allocator to the esp-system and refactored it moving all platform specific code to its place Closes IDF-1913 See merge request espressif/esp-idf!10267
This commit is contained in:
commit
83a7891f84
@ -19,7 +19,6 @@ else()
|
|||||||
"dport_access.c"
|
"dport_access.c"
|
||||||
"esp_himem.c"
|
"esp_himem.c"
|
||||||
"hw_random.c"
|
"hw_random.c"
|
||||||
"intr_alloc.c"
|
|
||||||
"spiram.c"
|
"spiram.c"
|
||||||
"spiram_psram.c"
|
"spiram_psram.c"
|
||||||
"system_api_esp32.c")
|
"system_api_esp32.c")
|
||||||
|
@ -17,7 +17,6 @@ else()
|
|||||||
"crosscore_int.c"
|
"crosscore_int.c"
|
||||||
"dport_access.c"
|
"dport_access.c"
|
||||||
"hw_random.c"
|
"hw_random.c"
|
||||||
"intr_alloc.c"
|
|
||||||
"spiram.c"
|
"spiram.c"
|
||||||
"spiram_psram.c"
|
"spiram_psram.c"
|
||||||
"system_api_esp32s2.c"
|
"system_api_esp32s2.c"
|
||||||
|
@ -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 <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#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<<level)
|
|
||||||
#define ESP_INTR_FLAG_LEVEL1 (1<<1) ///< Accept a Level 1 interrupt vector (lowest priority)
|
|
||||||
#define ESP_INTR_FLAG_LEVEL2 (1<<2) ///< Accept a Level 2 interrupt vector
|
|
||||||
#define ESP_INTR_FLAG_LEVEL3 (1<<3) ///< Accept a Level 3 interrupt vector
|
|
||||||
#define ESP_INTR_FLAG_LEVEL4 (1<<4) ///< Accept a Level 4 interrupt vector
|
|
||||||
#define ESP_INTR_FLAG_LEVEL5 (1<<5) ///< Accept a Level 5 interrupt vector
|
|
||||||
#define ESP_INTR_FLAG_LEVEL6 (1<<6) ///< Accept a Level 6 interrupt vector
|
|
||||||
#define ESP_INTR_FLAG_NMI (1<<7) ///< Accept a Level 7 interrupt vector (highest priority)
|
|
||||||
#define ESP_INTR_FLAG_SHARED (1<<8) ///< Interrupt can be shared between ISRs
|
|
||||||
#define ESP_INTR_FLAG_EDGE (1<<9) ///< Edge-triggered interrupt
|
|
||||||
#define ESP_INTR_FLAG_IRAM (1<<10) ///< ISR can be called if cache is disabled
|
|
||||||
#define ESP_INTR_FLAG_INTRDISABLED (1<<11) ///< Return with this interrupt disabled
|
|
||||||
|
|
||||||
#define ESP_INTR_FLAG_LOWMED (ESP_INTR_FLAG_LEVEL1|ESP_INTR_FLAG_LEVEL2|ESP_INTR_FLAG_LEVEL3) ///< Low and medium prio interrupts. These can be handled in C.
|
|
||||||
#define ESP_INTR_FLAG_HIGH (ESP_INTR_FLAG_LEVEL4|ESP_INTR_FLAG_LEVEL5|ESP_INTR_FLAG_LEVEL6|ESP_INTR_FLAG_NMI) ///< High level interrupts. Need to be handled in assembly.
|
|
||||||
|
|
||||||
#define ESP_INTR_FLAG_LEVELMASK (ESP_INTR_FLAG_LEVEL1|ESP_INTR_FLAG_LEVEL2|ESP_INTR_FLAG_LEVEL3| \
|
|
||||||
ESP_INTR_FLAG_LEVEL4|ESP_INTR_FLAG_LEVEL5|ESP_INTR_FLAG_LEVEL6| \
|
|
||||||
ESP_INTR_FLAG_NMI) ///< Mask for all level flags
|
|
||||||
|
|
||||||
|
|
||||||
/** @addtogroup Intr_Alloc_Pseudo_Src
|
|
||||||
* @{
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The esp_intr_alloc* functions can allocate an int for all ETS_*_INTR_SOURCE interrupt sources that
|
|
||||||
* are routed through the interrupt mux. Apart from these sources, each core also has some internal
|
|
||||||
* 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_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
|
|
||||||
|
|
||||||
/**@}*/
|
|
||||||
|
|
||||||
/** Provides SystemView with positive IRQ IDs, otherwise scheduler events are not shown properly
|
|
||||||
*/
|
|
||||||
#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<<inum))
|
|
||||||
|
|
||||||
/** Disable interrupt by interrupt number */
|
|
||||||
#define ESP_INTR_DISABLE(inum) xt_ints_off((1<<inum))
|
|
||||||
|
|
||||||
/** Function prototype for interrupt handler function */
|
|
||||||
typedef void (*intr_handler_t)(void *arg);
|
|
||||||
|
|
||||||
/** Interrupt handler associated data structure */
|
|
||||||
typedef struct intr_handle_data_t intr_handle_data_t;
|
|
||||||
|
|
||||||
/** Handle to an interrupt handler */
|
|
||||||
typedef intr_handle_data_t* intr_handle_t ;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Mark an interrupt as a shared interrupt
|
|
||||||
*
|
|
||||||
* This will mark a certain interrupt on the specified CPU as
|
|
||||||
* an interrupt that can be used to hook shared interrupt handlers
|
|
||||||
* to.
|
|
||||||
*
|
|
||||||
* @param intno The number of the interrupt (0-31)
|
|
||||||
* @param cpu CPU on which the interrupt should be marked as shared (0 or 1)
|
|
||||||
* @param is_in_iram Shared interrupt is for handlers that reside in IRAM and
|
|
||||||
* the int can be left enabled while the flash cache is disabled.
|
|
||||||
*
|
|
||||||
* @return ESP_ERR_INVALID_ARG if cpu or intno is invalid
|
|
||||||
* ESP_OK otherwise
|
|
||||||
*/
|
|
||||||
esp_err_t esp_intr_mark_shared(int intno, int cpu, bool is_in_iram);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Reserve an interrupt to be used outside of this framework
|
|
||||||
*
|
|
||||||
* This will mark a certain interrupt on the specified CPU as
|
|
||||||
* reserved, not to be allocated for any reason.
|
|
||||||
*
|
|
||||||
* @param intno The number of the interrupt (0-31)
|
|
||||||
* @param cpu CPU on which the interrupt should be marked as shared (0 or 1)
|
|
||||||
*
|
|
||||||
* @return ESP_ERR_INVALID_ARG if cpu or intno is invalid
|
|
||||||
* ESP_OK otherwise
|
|
||||||
*/
|
|
||||||
esp_err_t esp_intr_reserve(int intno, int cpu);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Allocate an interrupt with the given parameters.
|
|
||||||
*
|
|
||||||
* This finds an interrupt that matches the restrictions as given in the flags
|
|
||||||
* parameter, maps the given interrupt source to it and hooks up the given
|
|
||||||
* interrupt handler (with optional argument) as well. If needed, it can return
|
|
||||||
* a handle for the interrupt as well.
|
|
||||||
*
|
|
||||||
* The interrupt will always be allocated on the core that runs this function.
|
|
||||||
*
|
|
||||||
* If ESP_INTR_FLAG_IRAM flag is used, and handler address is not in IRAM or
|
|
||||||
* RTC_FAST_MEM, then ESP_ERR_INVALID_ARG is returned.
|
|
||||||
*
|
|
||||||
* @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 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(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
|
|
@ -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 <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
|
||||||
#include "freertos/FreeRTOS.h"
|
|
||||||
#include "freertos/task.h"
|
|
||||||
#include <esp_types.h>
|
|
||||||
#include "esp_err.h"
|
|
||||||
#include "esp_log.h"
|
|
||||||
#include "esp_intr_alloc.h"
|
|
||||||
#include "esp_attr.h"
|
|
||||||
#include "soc/soc.h"
|
|
||||||
#include <limits.h>
|
|
||||||
#include <assert.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_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<<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_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<<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_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);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1,291 +0,0 @@
|
|||||||
/*
|
|
||||||
Tests for the interrupt allocator.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
#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");
|
|
||||||
}
|
|
@ -17,7 +17,7 @@ else()
|
|||||||
"crosscore_int.c"
|
"crosscore_int.c"
|
||||||
"dport_access.c"
|
"dport_access.c"
|
||||||
"hw_random.c"
|
"hw_random.c"
|
||||||
"intr_alloc.c"
|
|
||||||
"memprot.c"
|
"memprot.c"
|
||||||
"spiram.c"
|
"spiram.c"
|
||||||
"spiram_psram.c"
|
"spiram_psram.c"
|
||||||
|
@ -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 <stdint.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#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<<level)
|
|
||||||
#define ESP_INTR_FLAG_LEVEL1 (1<<1) ///< Accept a Level 1 interrupt vector (lowest priority)
|
|
||||||
#define ESP_INTR_FLAG_LEVEL2 (1<<2) ///< Accept a Level 2 interrupt vector
|
|
||||||
#define ESP_INTR_FLAG_LEVEL3 (1<<3) ///< Accept a Level 3 interrupt vector
|
|
||||||
#define ESP_INTR_FLAG_LEVEL4 (1<<4) ///< Accept a Level 4 interrupt vector
|
|
||||||
#define ESP_INTR_FLAG_LEVEL5 (1<<5) ///< Accept a Level 5 interrupt vector
|
|
||||||
#define ESP_INTR_FLAG_LEVEL6 (1<<6) ///< Accept a Level 6 interrupt vector
|
|
||||||
#define ESP_INTR_FLAG_NMI (1<<7) ///< Accept a Level 7 interrupt vector (highest priority)
|
|
||||||
#define ESP_INTR_FLAG_SHARED (1<<8) ///< Interrupt can be shared between ISRs
|
|
||||||
#define ESP_INTR_FLAG_EDGE (1<<9) ///< Edge-triggered interrupt
|
|
||||||
#define ESP_INTR_FLAG_IRAM (1<<10) ///< ISR can be called if cache is disabled
|
|
||||||
#define ESP_INTR_FLAG_INTRDISABLED (1<<11) ///< Return with this interrupt disabled
|
|
||||||
|
|
||||||
#define ESP_INTR_FLAG_LOWMED (ESP_INTR_FLAG_LEVEL1|ESP_INTR_FLAG_LEVEL2|ESP_INTR_FLAG_LEVEL3) ///< Low and medium prio interrupts. These can be handled in C.
|
|
||||||
#define ESP_INTR_FLAG_HIGH (ESP_INTR_FLAG_LEVEL4|ESP_INTR_FLAG_LEVEL5|ESP_INTR_FLAG_LEVEL6|ESP_INTR_FLAG_NMI) ///< High level interrupts. Need to be handled in assembly.
|
|
||||||
|
|
||||||
#define ESP_INTR_FLAG_LEVELMASK (ESP_INTR_FLAG_LEVEL1|ESP_INTR_FLAG_LEVEL2|ESP_INTR_FLAG_LEVEL3| \
|
|
||||||
ESP_INTR_FLAG_LEVEL4|ESP_INTR_FLAG_LEVEL5|ESP_INTR_FLAG_LEVEL6| \
|
|
||||||
ESP_INTR_FLAG_NMI) ///< Mask for all level flags
|
|
||||||
/**@}*/
|
|
||||||
|
|
||||||
|
|
||||||
/** @addtogroup Intr_Alloc_Pseudo_Src
|
|
||||||
* @{
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The esp_intr_alloc* functions can allocate an int for all ETS_*_INTR_SOURCE interrupt sources that
|
|
||||||
* are routed through the interrupt mux. Apart from these sources, each core also has some internal
|
|
||||||
* 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_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
|
|
||||||
|
|
||||||
/**@}*/
|
|
||||||
|
|
||||||
// This is used to provide SystemView with positive IRQ IDs, otherwise sheduler events are not shown properly
|
|
||||||
#define ETS_INTERNAL_INTR_SOURCE_OFF (-ETS_INTERNAL_PROFILING_INTR_SOURCE)
|
|
||||||
|
|
||||||
#define ESP_INTR_ENABLE(inum) xt_ints_on((1<<inum))
|
|
||||||
#define ESP_INTR_DISABLE(inum) xt_ints_off((1<<inum))
|
|
||||||
|
|
||||||
typedef void (*intr_handler_t)(void *arg);
|
|
||||||
|
|
||||||
|
|
||||||
typedef struct intr_handle_data_t intr_handle_data_t;
|
|
||||||
typedef intr_handle_data_t *intr_handle_t ;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Mark an interrupt as a shared interrupt
|
|
||||||
*
|
|
||||||
* This will mark a certain interrupt on the specified CPU as
|
|
||||||
* an interrupt that can be used to hook shared interrupt handlers
|
|
||||||
* to.
|
|
||||||
*
|
|
||||||
* @param intno The number of the interrupt (0-31)
|
|
||||||
* @param cpu CPU on which the interrupt should be marked as shared (0 or 1)
|
|
||||||
* @param is_in_iram Shared interrupt is for handlers that reside in IRAM and
|
|
||||||
* the int can be left enabled while the flash cache is disabled.
|
|
||||||
*
|
|
||||||
* @return ESP_ERR_INVALID_ARG if cpu or intno is invalid
|
|
||||||
* ESP_OK otherwise
|
|
||||||
*/
|
|
||||||
esp_err_t esp_intr_mark_shared(int intno, int cpu, bool is_in_iram);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Reserve an interrupt to be used outside of this framework
|
|
||||||
*
|
|
||||||
* This will mark a certain interrupt on the specified CPU as
|
|
||||||
* reserved, not to be allocated for any reason.
|
|
||||||
*
|
|
||||||
* @param intno The number of the interrupt (0-31)
|
|
||||||
* @param cpu CPU on which the interrupt should be marked as shared (0 or 1)
|
|
||||||
*
|
|
||||||
* @return ESP_ERR_INVALID_ARG if cpu or intno is invalid
|
|
||||||
* ESP_OK otherwise
|
|
||||||
*/
|
|
||||||
esp_err_t esp_intr_reserve(int intno, int cpu);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Allocate an interrupt with the given parameters.
|
|
||||||
*
|
|
||||||
* This finds an interrupt that matches the restrictions as given in the flags
|
|
||||||
* parameter, maps the given interrupt source to it and hooks up the given
|
|
||||||
* interrupt handler (with optional argument) as well. If needed, it can return
|
|
||||||
* a handle for the interrupt as well.
|
|
||||||
*
|
|
||||||
* The interrupt will always be allocated on the core that runs this function.
|
|
||||||
*
|
|
||||||
* If ESP_INTR_FLAG_IRAM flag is used, and handler address is not in IRAM or
|
|
||||||
* RTC_FAST_MEM, then ESP_ERR_INVALID_ARG is returned.
|
|
||||||
*
|
|
||||||
* @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 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(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
|
|
@ -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 <stdint.h>
|
|
||||||
#include <stdio.h>
|
|
||||||
#include <stdlib.h>
|
|
||||||
#include <stdbool.h>
|
|
||||||
#include <string.h>
|
|
||||||
#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);
|
|
||||||
}
|
|
@ -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
|
INCLUDE_DIRS include
|
||||||
PRIV_REQUIRES spi_flash
|
PRIV_REQUIRES spi_flash
|
||||||
# [refactor-todo] requirements due to init code,
|
# [refactor-todo] requirements due to init code,
|
||||||
|
@ -18,7 +18,6 @@
|
|||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
#include "freertos/xtensa_api.h"
|
|
||||||
|
|
||||||
#ifdef __cplusplus
|
#ifdef __cplusplus
|
||||||
extern "C" {
|
extern "C" {
|
||||||
@ -68,9 +67,9 @@ extern "C" {
|
|||||||
* sources that do not pass through the interrupt mux. To allocate an interrupt for these sources,
|
* sources that do not pass through the interrupt mux. To allocate an interrupt for these sources,
|
||||||
* pass these pseudo-sources to the functions.
|
* pass these pseudo-sources to the functions.
|
||||||
*/
|
*/
|
||||||
#define ETS_INTERNAL_TIMER0_INTR_SOURCE -1 ///< Xtensa timer 0 interrupt source
|
#define ETS_INTERNAL_TIMER0_INTR_SOURCE -1 ///< Platform timer 0 interrupt source
|
||||||
#define ETS_INTERNAL_TIMER1_INTR_SOURCE -2 ///< Xtensa timer 1 interrupt source
|
#define ETS_INTERNAL_TIMER1_INTR_SOURCE -2 ///< Platform timer 1 interrupt source
|
||||||
#define ETS_INTERNAL_TIMER2_INTR_SOURCE -3 ///< Xtensa timer 2 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_SW0_INTR_SOURCE -4 ///< Software int source 1
|
||||||
#define ETS_INTERNAL_SW1_INTR_SOURCE -5 ///< Software int source 2
|
#define ETS_INTERNAL_SW1_INTR_SOURCE -5 ///< Software int source 2
|
||||||
#define ETS_INTERNAL_PROFILING_INTR_SOURCE -6 ///< Int source for profiling
|
#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)
|
#define ETS_INTERNAL_INTR_SOURCE_OFF (-ETS_INTERNAL_PROFILING_INTR_SOURCE)
|
||||||
|
|
||||||
/** Enable interrupt by interrupt number */
|
/** Enable interrupt by interrupt number */
|
||||||
#define ESP_INTR_ENABLE(inum) xt_ints_on((1<<inum))
|
#define ESP_INTR_ENABLE(inum) esp_intr_enable_source(inum)
|
||||||
|
|
||||||
/** Disable interrupt by interrupt number */
|
/** Disable interrupt by interrupt number */
|
||||||
#define ESP_INTR_DISABLE(inum) xt_ints_off((1<<inum))
|
#define ESP_INTR_DISABLE(inum) esp_intr_disable_source(inum)
|
||||||
|
|
||||||
/** Function prototype for interrupt handler function */
|
/** Function prototype for interrupt handler function */
|
||||||
typedef void (*intr_handler_t)(void *arg);
|
typedef void (*intr_handler_t)(void *arg);
|
||||||
@ -290,12 +289,23 @@ esp_err_t esp_intr_set_in_iram(intr_handle_t handle, bool is_in_iram);
|
|||||||
*/
|
*/
|
||||||
void esp_intr_noniram_disable(void);
|
void esp_intr_noniram_disable(void);
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Re-enable interrupts disabled by esp_intr_noniram_disable
|
* @brief Re-enable interrupts disabled by esp_intr_noniram_disable
|
||||||
*/
|
*/
|
||||||
void esp_intr_noniram_enable(void);
|
void esp_intr_noniram_enable(void);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief enable the interrupt source based on its number
|
||||||
|
* @param inum interrupt number from 0 to 31
|
||||||
|
*/
|
||||||
|
void esp_intr_enable_source(int inum);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief disable the interrupt source based on its number
|
||||||
|
* @param inum interrupt number from 0 to 31
|
||||||
|
*/
|
||||||
|
void esp_intr_disable_source(int inum);
|
||||||
|
|
||||||
/**@}*/
|
/**@}*/
|
||||||
|
|
||||||
|
|
@ -12,31 +12,28 @@
|
|||||||
// See the License for the specific language governing permissions and
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// limitations under the License.
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <esp_types.h>
|
||||||
|
#include <limits.h>
|
||||||
|
#include <assert.h>
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/task.h"
|
#include "freertos/task.h"
|
||||||
#include <esp_types.h>
|
|
||||||
#include "esp_err.h"
|
#include "esp_err.h"
|
||||||
//#define LOG_LOCAL_LEVEL ESP_LOG_VERBOSE
|
|
||||||
#include "esp_log.h"
|
#include "esp_log.h"
|
||||||
#include "esp_intr_alloc.h"
|
#include "esp_intr_alloc.h"
|
||||||
#include "esp_attr.h"
|
#include "esp_attr.h"
|
||||||
#include <limits.h>
|
#include "hal/cpu_hal.h"
|
||||||
#include <assert.h>
|
#include "hal/interrupt_controller_hal.h"
|
||||||
#include "soc/soc.h"
|
|
||||||
|
|
||||||
#if !CONFIG_FREERTOS_UNICORE
|
#if !CONFIG_FREERTOS_UNICORE
|
||||||
#include "esp_ipc.h"
|
#include "esp_ipc.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
static const char* TAG = "intr_alloc";
|
static const char* TAG = "intr_alloc";
|
||||||
|
|
||||||
#define ETS_INTERNAL_TIMER0_INTR_NO 6
|
#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_SW1_INTR_NO 29
|
||||||
#define ETS_INTERNAL_PROFILING_INTR_NO 11
|
#define ETS_INTERNAL_PROFILING_INTR_NO 11
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Define this to debug the choices made when allocating the interrupt. This leads to much debugging
|
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
|
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)
|
# define ALCHLOG(...) do {} while (0)
|
||||||
#endif
|
#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 shared_vector_desc_t shared_vector_desc_t;
|
||||||
typedef struct vector_desc_t 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;
|
shared_vector_desc_t *next;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
#define VECDESC_FL_RESERVED (1<<0)
|
#define VECDESC_FL_RESERVED (1<<0)
|
||||||
#define VECDESC_FL_INIRAM (1<<1)
|
#define VECDESC_FL_INIRAM (1<<1)
|
||||||
#define VECDESC_FL_SHARED (1<<2)
|
#define VECDESC_FL_SHARED (1<<2)
|
||||||
@ -176,10 +100,11 @@ struct non_shared_isr_arg_t {
|
|||||||
static vector_desc_t *vector_desc_head = NULL;
|
static vector_desc_t *vector_desc_head = NULL;
|
||||||
|
|
||||||
//This bitmask has an 1 if the int should be disabled when the flash is disabled.
|
//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.
|
//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 uint32_t non_iram_int_disabled[SOC_CPU_CORES_NUM];
|
||||||
static bool non_iram_int_disabled_flag[portNUM_PROCESSORS];
|
static bool non_iram_int_disabled_flag[SOC_CPU_CORES_NUM];
|
||||||
|
|
||||||
#if CONFIG_SYSVIEW_ENABLE
|
#if CONFIG_SYSVIEW_ENABLE
|
||||||
extern uint32_t port_switch_flag[];
|
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)
|
esp_err_t esp_intr_mark_shared(int intno, int cpu, bool is_int_ram)
|
||||||
{
|
{
|
||||||
if (intno>31) return ESP_ERR_INVALID_ARG;
|
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);
|
portENTER_CRITICAL(&spinlock);
|
||||||
vector_desc_t *vd=get_desc_for_int(intno, cpu);
|
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)
|
esp_err_t esp_intr_reserve(int intno, int cpu)
|
||||||
{
|
{
|
||||||
if (intno>31) return ESP_ERR_INVALID_ARG;
|
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);
|
portENTER_CRITICAL(&spinlock);
|
||||||
vector_desc_t *vd=get_desc_for_int(intno, cpu);
|
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;
|
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)
|
static bool is_vect_desc_usable(vector_desc_t *vd, int flags, int cpu, int force)
|
||||||
{
|
{
|
||||||
//Check if interrupt is not reserved by design
|
//Check if interrupt is not reserved by design
|
||||||
int x = vd->intno;
|
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");
|
ALCHLOG("....Unusable: reserved");
|
||||||
return false;
|
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");
|
ALCHLOG("....Unusable: special-purpose int");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
//Check if the interrupt level is acceptable
|
//Check if the interrupt level is acceptable
|
||||||
if (!(flags&(1<<int_desc[x].level))) {
|
if (!(flags&(1<<interrupt_controller_hal_get_level(x)))) {
|
||||||
ALCHLOG("....Unusable: incompatible level");
|
ALCHLOG("....Unusable: incompatible level");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
//check if edge/level type matches what we want
|
//check if edge/level type matches what we want
|
||||||
if (((flags&ESP_INTR_FLAG_EDGE) && (int_desc[x].type==INTTP_LEVEL)) ||
|
if (((flags&ESP_INTR_FLAG_EDGE) && (interrupt_controller_hal_get_type(x)==INTTP_LEVEL)) ||
|
||||||
(((!(flags&ESP_INTR_FLAG_EDGE)) && (int_desc[x].type==INTTP_EDGE)))) {
|
(((!(flags&ESP_INTR_FLAG_EDGE)) && (interrupt_controller_hal_get_type(x)==INTTP_EDGE)))) {
|
||||||
ALCHLOG("....Unusable: incompatible trigger type");
|
ALCHLOG("....Unusable: incompatible trigger type");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -368,8 +276,8 @@ static bool is_vect_desc_usable(vector_desc_t *vd, int flags, int cpu, int force
|
|||||||
ALCHLOG("...Unusable: int is shared, we need non-shared.");
|
ALCHLOG("...Unusable: int is shared, we need non-shared.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (int_has_handler(x, cpu)) {
|
} else if (interrupt_controller_hal_has_handler(x, cpu)) {
|
||||||
//Check if interrupt already is allocated by xt_set_interrupt_handler
|
//Check if interrupt already is allocated by interrupt_controller_hal_set_int_handler
|
||||||
ALCHLOG("....Unusable: already allocated");
|
ALCHLOG("....Unusable: already allocated");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -386,11 +294,11 @@ static int get_available_int(int flags, int cpu, int force, int source)
|
|||||||
int best=-1;
|
int best=-1;
|
||||||
int bestLevel=9;
|
int bestLevel=9;
|
||||||
int bestSharedCt=INT_MAX;
|
int bestSharedCt=INT_MAX;
|
||||||
|
|
||||||
//Default vector desc, for vectors not in the linked list
|
//Default vector desc, for vectors not in the linked list
|
||||||
vector_desc_t empty_vect_desc;
|
vector_desc_t empty_vect_desc;
|
||||||
memset(&empty_vect_desc, 0, sizeof(vector_desc_t));
|
memset(&empty_vect_desc, 0, sizeof(vector_desc_t));
|
||||||
|
|
||||||
|
|
||||||
//Level defaults to any low/med interrupt
|
//Level defaults to any low/med interrupt
|
||||||
if (!(flags&ESP_INTR_FLAG_LEVELMASK)) flags|=ESP_INTR_FLAG_LOWMED;
|
if (!(flags&ESP_INTR_FLAG_LEVELMASK)) flags|=ESP_INTR_FLAG_LOWMED;
|
||||||
|
|
||||||
@ -436,8 +344,8 @@ static int get_available_int(int flags, int cpu, int force, int source)
|
|||||||
}
|
}
|
||||||
|
|
||||||
ALCHLOG("Int %d reserved %d level %d %s hasIsr %d",
|
ALCHLOG("Int %d reserved %d level %d %s hasIsr %d",
|
||||||
x, int_desc[x].cpuflags[cpu]==INTDESC_RESVD, int_desc[x].level,
|
x, interrupt_controller_hal_get_cpu_desc_flags(x,cpu)==INTDESC_RESVD, interrupt_controller_hal_get_level(x),
|
||||||
int_desc[x].type==INTTP_LEVEL?"LEVEL":"EDGE", int_has_handler(x, cpu));
|
interrupt_controller_hal_get_type(x)==INTTP_LEVEL?"LEVEL":"EDGE", interrupt_controller_hal_has_handler(x, cpu));
|
||||||
|
|
||||||
if ( !is_vect_desc_usable(vd, flags, cpu, force) ) continue;
|
if ( !is_vect_desc_usable(vd, flags, cpu, force) ) continue;
|
||||||
|
|
||||||
@ -454,11 +362,11 @@ static int get_available_int(int flags, int cpu, int force, int source)
|
|||||||
no++;
|
no++;
|
||||||
svdesc=svdesc->next;
|
svdesc=svdesc->next;
|
||||||
}
|
}
|
||||||
if (no<bestSharedCt || bestLevel>int_desc[x].level) {
|
if (no<bestSharedCt || bestLevel>interrupt_controller_hal_get_level(x)) {
|
||||||
//Seems like this shared vector is both okay and has the least amount of ISRs already attached to it.
|
//Seems like this shared vector is both okay and has the least amount of ISRs already attached to it.
|
||||||
best=x;
|
best=x;
|
||||||
bestSharedCt=no;
|
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);
|
ALCHLOG("...int %d more usable as a shared int: has %d existing vectors", x, no);
|
||||||
} else {
|
} else {
|
||||||
ALCHLOG("...worse than int %d", best);
|
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
|
//We haven't found a feasible shared interrupt yet. This one is still free and usable, even if
|
||||||
//not marked as shared.
|
//not marked as shared.
|
||||||
//Remember it in case we don't find any other shared interrupt that qualifies.
|
//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;
|
best=x;
|
||||||
bestLevel=int_desc[x].level;
|
bestLevel=interrupt_controller_hal_get_level(x);
|
||||||
ALCHLOG("...int %d usable as a new shared int", x);
|
ALCHLOG("...int %d usable as a new shared int", x);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -479,9 +387,9 @@ static int get_available_int(int flags, int cpu, int force, int source)
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//Seems this interrupt is feasible. Select it and break out of the loop; no need to search further.
|
//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;
|
best=x;
|
||||||
bestLevel=int_desc[x].level;
|
bestLevel=interrupt_controller_hal_get_level(x);
|
||||||
} else {
|
} else {
|
||||||
ALCHLOG("...worse than int %d", best);
|
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);
|
sh_vec->isr(sh_vec->arg);
|
||||||
#if CONFIG_SYSVIEW_ENABLE
|
#if CONFIG_SYSVIEW_ENABLE
|
||||||
// check if we will return to scheduler or to interrupted task after ISR
|
// 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();
|
traceISR_EXIT();
|
||||||
}
|
}
|
||||||
#endif
|
#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
|
// when CONFIG_SYSVIEW_ENABLE = 0 ISRs for non-shared IRQs are called without spinlock
|
||||||
ns_isr_arg->isr(ns_isr_arg->isr_arg);
|
ns_isr_arg->isr(ns_isr_arg->isr_arg);
|
||||||
// check if we will return to scheduler or to interrupted task after ISR
|
// 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();
|
traceISR_EXIT();
|
||||||
}
|
}
|
||||||
portEXIT_CRITICAL_ISR(&spinlock);
|
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;
|
intr_handle_data_t *ret=NULL;
|
||||||
int force=-1;
|
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.
|
//Shared interrupts should be level-triggered.
|
||||||
if ((flags&ESP_INTR_FLAG_SHARED) && (flags&ESP_INTR_FLAG_EDGE)) return ESP_ERR_INVALID_ARG;
|
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.
|
//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;
|
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
|
//Check 'special' interrupt sources. These are tied to one specific interrupt, so we
|
||||||
//have to force get_free_int to only look at that.
|
//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;
|
if (ret==NULL) return ESP_ERR_NO_MEM;
|
||||||
|
|
||||||
portENTER_CRITICAL(&spinlock);
|
portENTER_CRITICAL(&spinlock);
|
||||||
int cpu=xPortGetCoreID();
|
int cpu=cpu_hal_get_core_id();
|
||||||
//See if we can find an interrupt that matches the flags.
|
//See if we can find an interrupt that matches the flags.
|
||||||
int intr=get_available_int(flags, cpu, force, source);
|
int intr=get_available_int(flags, cpu, force, source);
|
||||||
if (intr==-1) {
|
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=handler;
|
||||||
ns_isr_arg->isr_arg=arg;
|
ns_isr_arg->isr_arg=arg;
|
||||||
ns_isr_arg->source=source;
|
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
|
#else
|
||||||
xt_set_interrupt_handler(intr, handler, arg);
|
interrupt_controller_hal_set_int_handler(intr, handler, arg);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
if (flags&ESP_INTR_FLAG_EDGE) xthal_set_intclear(1 << intr);
|
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
|
#if !CONFIG_FREERTOS_UNICORE
|
||||||
//Assign this routine to the core where this interrupt is allocated on.
|
//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);
|
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;
|
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");
|
ESP_EARLY_LOGV(TAG, "esp_intr_free: Disabling int, killing handler");
|
||||||
#if CONFIG_SYSVIEW_ENABLE
|
#if CONFIG_SYSVIEW_ENABLE
|
||||||
if (!free_shared_vector) {
|
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) {
|
if (isr_arg) {
|
||||||
free(isr_arg);
|
free(isr_arg);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
//Reset to normal handler
|
//Reset to normal handler:
|
||||||
xt_set_interrupt_handler(handle->vector_desc->intno, xt_unhandled_interrupt, (void*)((int)handle->vector_desc->intno));
|
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
|
//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
|
//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.
|
//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);
|
intr_matrix_set(handle->vector_desc->cpu, source, handle->vector_desc->intno);
|
||||||
} else {
|
} else {
|
||||||
//Re-enable using cpu int ena reg
|
//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);
|
ESP_INTR_ENABLE(handle->vector_desc->intno);
|
||||||
}
|
}
|
||||||
portEXIT_CRITICAL_SAFE(&spinlock);
|
portEXIT_CRITICAL_SAFE(&spinlock);
|
||||||
@ -857,7 +765,7 @@ esp_err_t IRAM_ATTR esp_intr_disable(intr_handle_t handle)
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
//Disable using per-cpu regs
|
//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);
|
portEXIT_CRITICAL_SAFE(&spinlock);
|
||||||
return ESP_ERR_INVALID_ARG; //Can only enable these ints on this cpu
|
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;
|
return ESP_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void IRAM_ATTR esp_intr_noniram_disable(void)
|
void IRAM_ATTR esp_intr_noniram_disable(void)
|
||||||
{
|
{
|
||||||
int oldint;
|
uint32_t oldint;
|
||||||
int cpu=xPortGetCoreID();
|
int cpu=cpu_hal_get_core_id();
|
||||||
int intmask=~non_iram_int_mask[cpu];
|
uint32_t intmask=~non_iram_int_mask[cpu];
|
||||||
if (non_iram_int_disabled_flag[cpu]) abort();
|
if (non_iram_int_disabled_flag[cpu]) abort();
|
||||||
non_iram_int_disabled_flag[cpu]=true;
|
non_iram_int_disabled_flag[cpu]=true;
|
||||||
asm volatile (
|
oldint = interrupt_controller_hal_disable_int_mask(intmask);
|
||||||
"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
|
//Save which ints we did disable
|
||||||
non_iram_int_disabled[cpu]=oldint&non_iram_int_mask[cpu];
|
non_iram_int_disabled[cpu]=oldint&non_iram_int_mask[cpu];
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRAM_ATTR esp_intr_noniram_enable(void)
|
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];
|
int intmask=non_iram_int_disabled[cpu];
|
||||||
if (!non_iram_int_disabled_flag[cpu]) abort();
|
if (!non_iram_int_disabled_flag[cpu]) abort();
|
||||||
non_iram_int_disabled_flag[cpu]=false;
|
non_iram_int_disabled_flag[cpu]=false;
|
||||||
asm volatile (
|
interrupt_controller_hal_enable_int_mask(intmask);
|
||||||
"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
|
//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) {
|
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) {
|
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);
|
||||||
|
}
|
@ -17,7 +17,7 @@
|
|||||||
#include "esp_intr_alloc.h"
|
#include "esp_intr_alloc.h"
|
||||||
#include "driver/periph_ctrl.h"
|
#include "driver/periph_ctrl.h"
|
||||||
#include "driver/timer.h"
|
#include "driver/timer.h"
|
||||||
|
#include "sdkconfig.h"
|
||||||
|
|
||||||
#define TIMER_DIVIDER 16 /*!< Hardware timer clock divider */
|
#define TIMER_DIVIDER 16 /*!< Hardware timer clock divider */
|
||||||
#define TIMER_SCALE (TIMER_BASE_CLK / TIMER_DIVIDER) /*!< used to calculate counter value */
|
#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 {
|
} else {
|
||||||
ctx->flag1 = true;
|
ctx->flag1 = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef CONFIG_IDF_TARGET_ESP32
|
||||||
SPI2.slave.trans_done = 0;
|
SPI2.slave.trans_done = 0;
|
||||||
|
#else
|
||||||
|
GPSPI2.slave.trans_done = 0;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void IRAM_ATTR int_handler2(void* arg)
|
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_alloc_test_ctx_t ctx = {false, false, false, false };
|
||||||
intr_handle_t handle1, handle2;
|
intr_handle_t handle1, handle2;
|
||||||
|
|
||||||
|
#ifdef CONFIG_IDF_TARGET_ESP32
|
||||||
//enable HSPI(spi2)
|
//enable HSPI(spi2)
|
||||||
periph_module_enable(PERIPH_HSPI_MODULE);
|
periph_module_enable(PERIPH_HSPI_MODULE);
|
||||||
|
#else
|
||||||
|
periph_module_enable(PERIPH_FSPI_MODULE);
|
||||||
|
#endif
|
||||||
|
|
||||||
esp_err_t r;
|
esp_err_t r;
|
||||||
r=esp_intr_alloc(ETS_SPI2_INTR_SOURCE, ESP_INTR_FLAG_SHARED, int_handler1, &ctx, &handle1);
|
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
|
//assign shared then
|
||||||
r=esp_intr_alloc(ETS_SPI2_INTR_SOURCE, ESP_INTR_FLAG_SHARED, int_handler2, &ctx, &handle2);
|
r=esp_intr_alloc(ETS_SPI2_INTR_SOURCE, ESP_INTR_FLAG_SHARED, int_handler2, &ctx, &handle2);
|
||||||
TEST_ESP_OK(r);
|
TEST_ESP_OK(r);
|
||||||
|
|
||||||
|
#ifdef CONFIG_IDF_TARGET_ESP32
|
||||||
SPI2.slave.trans_inten = 1;
|
SPI2.slave.trans_inten = 1;
|
||||||
|
#else
|
||||||
|
GPSPI2.slave.int_trans_done_en = 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
printf("trigger first time.\n");
|
printf("trigger first time.\n");
|
||||||
|
|
||||||
|
#ifdef CONFIG_IDF_TARGET_ESP32
|
||||||
SPI2.slave.trans_done = 1;
|
SPI2.slave.trans_done = 1;
|
||||||
|
#else
|
||||||
|
GPSPI2.slave.trans_done = 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
vTaskDelay(100);
|
vTaskDelay(100);
|
||||||
TEST_ASSERT( ctx.flag1 && ctx.flag2 );
|
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);
|
r=esp_intr_free(handle2);
|
||||||
|
|
||||||
printf("trigger second time.\n");
|
printf("trigger second time.\n");
|
||||||
|
|
||||||
|
#ifdef CONFIG_IDF_TARGET_ESP32
|
||||||
SPI2.slave.trans_done = 1;
|
SPI2.slave.trans_done = 1;
|
||||||
|
#else
|
||||||
|
GPSPI2.slave.trans_done = 1;
|
||||||
|
#endif
|
||||||
|
|
||||||
vTaskDelay(500);
|
vTaskDelay(500);
|
||||||
TEST_ASSERT( ctx.flag3 && !ctx.flag4 );
|
TEST_ASSERT( ctx.flag3 && !ctx.flag4 );
|
||||||
printf("test passed.\n");
|
printf("test passed.\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
#ifndef CONFIG_FREERTOS_UNICORE
|
#ifndef CONFIG_FREERTOS_UNICORE
|
||||||
|
|
||||||
void isr_free_task(void *param)
|
void isr_free_task(void *param)
|
@ -9,8 +9,6 @@ set(srcs
|
|||||||
"xtensa/portasm.S"
|
"xtensa/portasm.S"
|
||||||
"xtensa/xtensa_context.S"
|
"xtensa/xtensa_context.S"
|
||||||
"xtensa/xtensa_init.c"
|
"xtensa/xtensa_init.c"
|
||||||
"xtensa/xtensa_intr_asm.S"
|
|
||||||
"xtensa/xtensa_intr.c"
|
|
||||||
"xtensa/xtensa_overlay_os_hook.c"
|
"xtensa/xtensa_overlay_os_hook.c"
|
||||||
"xtensa/xtensa_vector_defaults.S"
|
"xtensa/xtensa_vector_defaults.S"
|
||||||
"xtensa/xtensa_vectors.S")
|
"xtensa/xtensa_vectors.S")
|
||||||
|
@ -86,13 +86,12 @@ extern "C" {
|
|||||||
#include <esp_heap_caps.h>
|
#include <esp_heap_caps.h>
|
||||||
#include "esp_rom_sys.h"
|
#include "esp_rom_sys.h"
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
|
#include "freertos/xtensa_api.h"
|
||||||
|
|
||||||
#ifdef CONFIG_LEGACY_INCLUDE_COMMON_HEADERS
|
#ifdef CONFIG_LEGACY_INCLUDE_COMMON_HEADERS
|
||||||
#include "soc/soc_memory_layout.h"
|
#include "soc/soc_memory_layout.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//#include "xtensa_context.h"
|
|
||||||
|
|
||||||
/*-----------------------------------------------------------
|
/*-----------------------------------------------------------
|
||||||
* Port specific definitions.
|
* Port specific definitions.
|
||||||
*
|
*
|
||||||
|
@ -1,130 +1,2 @@
|
|||||||
/*******************************************************************************
|
/* This header file has been moved, please include <xtensa/xtensa_api.h> in future */
|
||||||
Copyright (c) 2006-2015 Cadence Design Systems Inc.
|
#include <xtensa/xtensa_api.h>
|
||||||
|
|
||||||
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 <xtensa/hal.h>
|
|
||||||
|
|
||||||
#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__ */
|
|
||||||
|
|
||||||
|
@ -1,387 +1,2 @@
|
|||||||
/*******************************************************************************
|
/* This header file has been moved, please include <xtensa/xtensa_context.h> in future */
|
||||||
Copyright (c) 2006-2015 Cadence Design Systems Inc.
|
#include <xtensa/xtensa_context.h>
|
||||||
|
|
||||||
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 <xtensa/coreasm.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include <xtensa/config/tie.h>
|
|
||||||
#include <xtensa/corebits.h>
|
|
||||||
#include <xtensa/config/system.h>
|
|
||||||
#include <xtensa/xtruntime-frames.h>
|
|
||||||
|
|
||||||
|
|
||||||
/* 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 */
|
|
||||||
|
|
@ -386,7 +386,8 @@ _frxt_tick_timer_init:
|
|||||||
call0 xt_ints_on
|
call0 xt_ints_on
|
||||||
#else
|
#else
|
||||||
movi a6, XT_TIMER_INTEN
|
movi a6, XT_TIMER_INTEN
|
||||||
call4 xt_ints_on
|
movi a3, xt_ints_on
|
||||||
|
callx4 a3
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
RET(16)
|
RET(16)
|
||||||
|
@ -30,7 +30,9 @@ if(NOT BOOTLOADER_BUILD)
|
|||||||
"spi_flash_hal.c"
|
"spi_flash_hal.c"
|
||||||
"spi_flash_hal_iram.c"
|
"spi_flash_hal_iram.c"
|
||||||
"soc_hal.c"
|
"soc_hal.c"
|
||||||
"twai_hal.c")
|
"twai_hal.c"
|
||||||
|
"interrupt_controller_hal.c"
|
||||||
|
"${target}/interrupt_descriptor_table.c")
|
||||||
|
|
||||||
if(${target} STREQUAL "esp32")
|
if(${target} STREQUAL "esp32")
|
||||||
list(APPEND srcs
|
list(APPEND srcs
|
||||||
|
105
components/hal/esp32/include/hal/interrupt_controller_ll.h
Normal file
105
components/hal/esp32/include/hal/interrupt_controller_ll.h
Normal file
@ -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 <stdint.h>
|
||||||
|
#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
|
75
components/hal/esp32/interrupt_descriptor_table.c
Normal file
75
components/hal/esp32/interrupt_descriptor_table.c
Normal file
@ -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;
|
||||||
|
}
|
105
components/hal/esp32s2/include/hal/interrupt_controller_ll.h
Normal file
105
components/hal/esp32s2/include/hal/interrupt_controller_ll.h
Normal file
@ -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 <stdint.h>
|
||||||
|
#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
|
75
components/hal/esp32s2/interrupt_descriptor_table.c
Normal file
75
components/hal/esp32s2/interrupt_descriptor_table.c
Normal file
@ -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;
|
||||||
|
}
|
105
components/hal/esp32s3/include/hal/interrupt_controller_ll.h
Normal file
105
components/hal/esp32s3/include/hal/interrupt_controller_ll.h
Normal file
@ -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 <stdint.h>
|
||||||
|
#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
|
75
components/hal/esp32s3/interrupt_descriptor_table.c
Normal file
75
components/hal/esp32s3/interrupt_descriptor_table.c
Normal file
@ -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;
|
||||||
|
}
|
170
components/hal/include/hal/interrupt_controller_hal.h
Normal file
170
components/hal/include/hal/interrupt_controller_hal.h
Normal file
@ -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 <stdbool.h>
|
||||||
|
#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
|
46
components/hal/include/hal/interrupt_controller_types.h
Normal file
46
components/hal/include/hal/interrupt_controller_types.h
Normal file
@ -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
|
33
components/hal/interrupt_controller_hal.c
Normal file
33
components/hal/interrupt_controller_hal.c
Normal file
@ -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]);
|
||||||
|
}
|
@ -11,6 +11,8 @@ else()
|
|||||||
"expression_with_stack_xtensa_asm.S"
|
"expression_with_stack_xtensa_asm.S"
|
||||||
"eri.c"
|
"eri.c"
|
||||||
"trax.c"
|
"trax.c"
|
||||||
|
"xtensa_intr.c"
|
||||||
|
"xtensa_intr_asm.S"
|
||||||
"${target}/trax_init.c"
|
"${target}/trax_init.c"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
181
components/xtensa/include/xtensa/xtensa_api.h
Normal file
181
components/xtensa/include/xtensa/xtensa_api.h
Normal file
@ -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 <stdbool.h>
|
||||||
|
#include <xtensa/hal.h>
|
||||||
|
#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__ */
|
||||||
|
|
387
components/xtensa/include/xtensa/xtensa_context.h
Normal file
387
components/xtensa/include/xtensa/xtensa_context.h
Normal file
@ -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 <xtensa/coreasm.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <xtensa/config/tie.h>
|
||||||
|
#include <xtensa/corebits.h>
|
||||||
|
#include <xtensa/config/system.h>
|
||||||
|
#include <xtensa/xtruntime-frames.h>
|
||||||
|
|
||||||
|
|
||||||
|
/* 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 */
|
||||||
|
|
@ -31,8 +31,8 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||||||
#include <xtensa/config/core.h>
|
#include <xtensa/config/core.h>
|
||||||
|
|
||||||
#include "freertos/FreeRTOS.h"
|
#include "freertos/FreeRTOS.h"
|
||||||
#include "freertos/xtensa_api.h"
|
|
||||||
#include "freertos/portable.h"
|
#include "freertos/portable.h"
|
||||||
|
#include "xtensa/xtensa_api.h"
|
||||||
#include "sdkconfig.h"
|
#include "sdkconfig.h"
|
||||||
#include "esp_rom_sys.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 */
|
/* 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());
|
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"
|
This function registers a handler for the specified interrupt. The "arg"
|
@ -29,8 +29,8 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|||||||
#include <xtensa/hal.h>
|
#include <xtensa/hal.h>
|
||||||
#include <xtensa/config/core.h>
|
#include <xtensa/config/core.h>
|
||||||
|
|
||||||
#include "xtensa_context.h"
|
#include "xtensa/xtensa_context.h"
|
||||||
#include "FreeRTOSConfig.h"
|
#include "freertos/FreeRTOSConfig.h"
|
||||||
|
|
||||||
#if XCHAL_HAVE_INTERRUPTS
|
#if XCHAL_HAVE_INTERRUPTS
|
||||||
|
|
@ -237,7 +237,7 @@ INPUT = \
|
|||||||
## Himem
|
## Himem
|
||||||
$(IDF_PATH)/components/esp32/include/esp32/himem.h \
|
$(IDF_PATH)/components/esp32/include/esp32/himem.h \
|
||||||
## Interrupt Allocation
|
## Interrupt Allocation
|
||||||
$(IDF_PATH)/components/$(IDF_TARGET)/include/esp_intr_alloc.h \
|
$(IDF_PATH)/components/esp_system/include/esp_intr_alloc.h \
|
||||||
## Watchdogs
|
## Watchdogs
|
||||||
## NOTE: for two lines below header_file.inc is not used
|
## NOTE: for two lines below header_file.inc is not used
|
||||||
$(IDF_PATH)/components/esp_common/include/esp_int_wdt.h \
|
$(IDF_PATH)/components/esp_common/include/esp_int_wdt.h \
|
||||||
|
@ -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`
|
- Port number, one of the two port numbers from :cpp:type:`i2c_port_t`
|
||||||
- Master or slave, selected from :cpp:type:`i2c_mode_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.
|
- (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:
|
.. _i2c-api-master-mode:
|
||||||
|
@ -75,7 +75,7 @@ I2C 驱动程序管理在 I2C 总线上设备的通信,该驱动程序具备
|
|||||||
- 端口号,从 :cpp:type:`i2c_port_t` 中二选一
|
- 端口号,从 :cpp:type:`i2c_port_t` 中二选一
|
||||||
- 主机或从机模式,从 :cpp:type:`i2c_mode_t` 中选择
|
- 主机或从机模式,从 :cpp:type:`i2c_mode_t` 中选择
|
||||||
- (仅限从机模式)分配用于在从机模式下发送和接收数据的缓存区大小。I2C 是一个以主机为中心的总线,数据只能根据主机的请求从从机传输到主机。因此,从机通常有一个发送缓存区,供从应用程序写入数据使用。数据保留在发送缓存区中,由主机自行读取。
|
- (仅限从机模式)分配用于在从机模式下发送和接收数据的缓存区大小。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:
|
.. _i2c-api-master-mode:
|
||||||
|
Loading…
Reference in New Issue
Block a user