Add disabling/enabling of shared interrupt, add testcase for interrupts

This commit is contained in:
Jeroen Domburg 2016-12-07 21:30:21 +08:00
parent 32fa94935d
commit ae8c37e0b6
16 changed files with 348 additions and 102 deletions

View file

@ -321,10 +321,10 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig)
return ESP_OK;
}
esp_err_t gpio_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags)
esp_err_t gpio_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags, gpio_isr_handle_t *handle)
{
GPIO_CHECK(fn, "GPIO ISR null", ESP_ERR_INVALID_ARG);
return esp_intr_alloc(ETS_GPIO_INTR_SOURCE, intr_alloc_flags, fn, arg, NULL);
return esp_intr_alloc(ETS_GPIO_INTR_SOURCE, intr_alloc_flags, fn, arg, handle);
}
/*only level interrupt can be used for wake-up function*/

View file

@ -23,6 +23,7 @@
#include "soc/gpio_sig_map.h"
#include "rom/gpio.h"
#include "esp_attr.h"
#include "esp_intr_alloc.h"
#ifdef __cplusplus
extern "C" {
@ -203,6 +204,9 @@ typedef enum {
GPIO_FLOATING, /*!< Pad floating */
} gpio_pull_mode_t;
typedef intr_handle_t gpio_isr_handle_t;
typedef void (*gpio_event_callback)(gpio_num_t gpio_intr_num);
/**
@ -352,7 +356,7 @@ esp_err_t gpio_wakeup_disable(gpio_num_t gpio_num);
* - ESP_OK Success ;
* - ESP_ERR_INVALID_ARG GPIO error
*/
esp_err_t gpio_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags);
esp_err_t gpio_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags, gpio_isr_handle_t *handle);

View file

@ -21,6 +21,7 @@
#include "soc/ledc_struct.h"
#include "driver/gpio.h"
#include "driver/periph_ctrl.h"
#include "esp_intr_alloc.h"
#ifdef __cplusplus
extern "C" {
@ -100,6 +101,7 @@ typedef struct {
uint32_t freq_hz; /*!< LEDC timer frequency(Hz)*/
} ledc_timer_config_t;
typedef intr_handle_t ledc_isr_handle_t;
/**
* @brief LEDC channel configuration
@ -268,7 +270,7 @@ esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty,
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Function pointer error.
*/
esp_err_t ledc_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags);
esp_err_t ledc_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags, ledc_isr_handle_t *handle);
/**
* @brief configure LEDC settings

View file

@ -12,6 +12,7 @@
#include "soc/pcnt_struct.h"
#include "soc/gpio_sig_map.h"
#include "driver/gpio.h"
#include "esp_intr_alloc.h"
#ifdef __cplusplus
extern "C" {
@ -76,6 +77,8 @@ typedef struct {
pcnt_channel_t channel; /*!< the PCNT channel */
} pcnt_config_t;
typedef intr_handle_t pcnt_isr_handle_t;
/**
* @brief Configure Pulse Counter unit
*
@ -223,7 +226,7 @@ esp_err_t pcnt_get_event_value(pcnt_unit_t unit, pcnt_evt_type_t evt_type, int16
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Function pointer error.
*/
esp_err_t pcnt_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags);
esp_err_t pcnt_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags, pcnt_isr_handle_t *handle);
/**
* @brief Configure PCNT pulse signal input pin and control input pin

View file

@ -117,6 +117,8 @@ typedef struct {
};
} rmt_config_t;
typedef intr_handle_t rmt_isr_handle_t;
/**
* @brief Set RMT clock divider, channel clock is divided from source clock.
*
@ -574,13 +576,25 @@ esp_err_t rmt_config(rmt_config_t* rmt_param);
* @param arg Parameter for handler function
* @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred)
* ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info.
* @param If non-zero, a handle to later clean up the ISR gets stored here.
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Function pointer error.
* - ESP_FAIL System driver installed, can not register ISR handler for RMT
*/
esp_err_t rmt_isr_register(void (* fn)(void* ), void * arg, int intr_alloc_flags);
esp_err_t rmt_isr_register(void (* fn)(void* ), void * arg, int intr_alloc_flags, rmt_isr_handle_t *handle);
/**
* @brief Deregister previously registered RMT interrupt handler
*
* @param handle Handle obtained from rmt_isr_register
*
* @return
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Handle invalid
*/
esp_err_t rmt_isr_deregister(rmt_isr_handle_t handle);
/**
* @brief Fill memory data of channel with given RMT items.

View file

@ -19,6 +19,7 @@
#include "soc/soc.h"
#include "soc/timer_group_reg.h"
#include "soc/timer_group_struct.h"
#include "esp_intr_alloc.h"
#ifdef __cplusplus
extern "C" {
@ -100,6 +101,13 @@ typedef struct {
uint16_t divider; /*!< Counter clock divider*/
} timer_config_t;
/**
* @brief Interrupt handle, used in order to free the isr after use.
* Aliases to an int handle for now.
*/
typedef intr_handle_t timer_isr_handle_t;
/**
* @brief Read the counter value of hardware timer.
*
@ -264,7 +272,7 @@ esp_err_t timer_set_alarm(timer_group_t group_num, timer_idx_t timer_num, timer_
* - ESP_OK Success
* - ESP_ERR_INVALID_ARG Parameter error
*/
esp_err_t timer_isr_register(timer_group_t group_num, timer_idx_t timer_num, void (*fn)(void*), void * arg, int intr_alloc_flags);
esp_err_t timer_isr_register(timer_group_t group_num, timer_idx_t timer_num, void (*fn)(void*), void * arg, int intr_alloc_flags, timer_isr_handle_t *handle);
/** @brief Initializes and configure the timer.
*

View file

@ -371,8 +371,7 @@ esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh);
* @param fn Interrupt handler function.
* @param arg parameter for handler function
* @param intr_alloc_flags Flags used to allocate the interrupt. One or multiple (ORred)
* ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info. Note that the UART
* driver at the moment does not work with a shared interrupt.
* ESP_INTR_FLAG_* values. See esp_intr_alloc.h for more info.
*
* @return
* - ESP_OK Success

View file

@ -114,12 +114,12 @@ static esp_err_t ledc_enable_intr_type(ledc_mode_t speed_mode, uint32_t channel,
return ESP_OK;
}
esp_err_t ledc_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags)
esp_err_t ledc_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags, ledc_isr_handle_t *handle)
{
esp_err_t ret;
LEDC_CHECK(fn, "ledc isr null", ESP_ERR_INVALID_ARG);
portENTER_CRITICAL(&ledc_spinlock);
ret=esp_intr_alloc(ETS_LEDC_INTR_SOURCE, intr_alloc_flags, fn, arg, NULL);
ret=esp_intr_alloc(ETS_LEDC_INTR_SOURCE, intr_alloc_flags, fn, arg, handle);
portEXIT_CRITICAL(&ledc_spinlock);
return ret;
}

View file

@ -267,9 +267,9 @@ esp_err_t pcnt_filter_disable(pcnt_unit_t unit)
return ESP_OK;
}
esp_err_t pcnt_isr_register(void (*fun)(void*), void * arg, int intr_alloc_flags)
esp_err_t pcnt_isr_register(void (*fun)(void*), void * arg, int intr_alloc_flags, pcnt_isr_handle_t *handle)
{
PCNT_CHECK(fun != NULL, PCNT_ADDRESS_ERR_STR, ESP_ERR_INVALID_ARG);
return esp_intr_alloc(ETS_PCNT_INTR_SOURCE, intr_alloc_flags, fun, arg, NULL);
return esp_intr_alloc(ETS_PCNT_INTR_SOURCE, intr_alloc_flags, fun, arg, handle);
}

View file

@ -46,6 +46,7 @@
static const char* RMT_TAG = "RMT";
static bool s_rmt_driver_installed = false;
static rmt_isr_handle_t s_rmt_driver_intr_handle;
#define RMT_CHECK(a, str, ret) if (!(a)) { \
ESP_LOGE(RMT_TAG,"%s:%d (%s):%s", __FILE__, __LINE__, __FUNCTION__, str); \
@ -473,17 +474,23 @@ esp_err_t rmt_fill_tx_items(rmt_channel_t channel, rmt_item32_t* item, uint16_t
return ESP_OK;
}
esp_err_t rmt_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags)
esp_err_t rmt_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags, rmt_isr_handle_t *handle)
{
esp_err_t ret;
RMT_CHECK((fn != NULL), RMT_ADDR_ERROR_STR, ESP_ERR_INVALID_ARG);
RMT_CHECK(s_rmt_driver_installed == false, "RMT DRIVER INSTALLED, CAN NOT REG ISR HANDLER", ESP_FAIL);
portENTER_CRITICAL(&rmt_spinlock);
ret=esp_intr_alloc(ETS_RMT_INTR_SOURCE, intr_alloc_flags, fn, arg, NULL);
ret=esp_intr_alloc(ETS_RMT_INTR_SOURCE, intr_alloc_flags, fn, arg, handle);
portEXIT_CRITICAL(&rmt_spinlock);
return ret;
}
esp_err_t rmt_isr_deregister(rmt_isr_handle_t handle)
{
return esp_intr_free(handle);
}
static int IRAM_ATTR rmt_get_mem_len(rmt_channel_t channel)
{
int block_num = RMT.conf_ch[channel].conf0.mem_size;
@ -615,7 +622,7 @@ esp_err_t rmt_driver_uninstall(rmt_channel_t channel)
free(p_rmt_obj[channel]);
p_rmt_obj[channel] = NULL;
s_rmt_driver_installed = false;
return ESP_OK;
return rmt_isr_deregister(s_rmt_driver_intr_handle);
}
esp_err_t rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int intr_alloc_flags)
@ -650,7 +657,7 @@ esp_err_t rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int intr
rmt_set_err_intr_en(channel, 1);
}
if(s_rmt_driver_installed == false) {
rmt_isr_register(rmt_driver_isr_default, NULL, intr_alloc_flags);
rmt_isr_register(rmt_driver_isr_default, NULL, intr_alloc_flags, &s_rmt_driver_intr_handle);
s_rmt_driver_installed = true;
}
rmt_set_tx_intr_en(channel, 1);

View file

@ -169,13 +169,15 @@ esp_err_t timer_set_alarm(timer_group_t group_num, timer_idx_t timer_num, timer_
}
esp_err_t timer_isr_register(timer_group_t group_num, timer_idx_t timer_num,
void (*fn)(void*), void * arg, int intr_alloc_flags)
void (*fn)(void*), void * arg, int intr_alloc_flags, timer_isr_handle_t *handle)
{
TIMER_CHECK(group_num < TIMER_GROUP_MAX, TIMER_GROUP_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_CHECK(timer_num < TIMER_MAX, TIMER_NUM_ERROR, ESP_ERR_INVALID_ARG);
TIMER_CHECK(fn != NULL, TIMER_PARAM_ADDR_ERROR, ESP_ERR_INVALID_ARG);
int intr_source = 0;
uint32_t status_reg = 0;
int mask = 0;
switch(group_num) {
case TIMER_GROUP_0:
default:
@ -184,6 +186,8 @@ esp_err_t timer_isr_register(timer_group_t group_num, timer_idx_t timer_num,
} else {
intr_source = ETS_TG0_T0_EDGE_INTR_SOURCE + timer_num;
}
status_reg = TIMG_INT_ST_TIMERS_REG(0);
mask = 1<<timer_num;
break;
case TIMER_GROUP_1:
if((intr_alloc_flags & ESP_INTR_FLAG_EDGE) == 0) {
@ -191,9 +195,11 @@ esp_err_t timer_isr_register(timer_group_t group_num, timer_idx_t timer_num,
} else {
intr_source = ETS_TG1_T0_EDGE_INTR_SOURCE + timer_num;
}
status_reg = TIMG_INT_ST_TIMERS_REG(1);
mask = 1<<timer_num;
break;
}
return esp_intr_alloc(intr_source, intr_alloc_flags, fn, arg, NULL);
return esp_intr_alloc_intrstatus(intr_source, intr_alloc_flags, status_reg, mask, fn, arg, handle);
}
esp_err_t timer_init(timer_group_t group_num, timer_idx_t timer_num, timer_config_t *config)

View file

@ -55,7 +55,7 @@ typedef struct {
uart_port_t uart_num; /*!< UART port number*/
int queue_size; /*!< UART event queue size*/
QueueHandle_t xQueueUart; /*!< UART queue handler*/
int_handle_t intr_handle; /*!< UART interrupt handle*/
intr_handle_t intr_handle; /*!< UART interrupt handle*/
//rx parameters
SemaphoreHandle_t rx_mux; /*!< UART RX data mutex*/
int rx_buf_size; /*!< RX ring buffer size */
@ -292,7 +292,6 @@ esp_err_t uart_isr_register(uart_port_t uart_num, void (*fn)(void*), void * arg,
{
int ret;
UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL);
UART_CHECK(((intr_alloc_flags & ESP_INTR_FLAG_SHARED)==0), "UART doesn't support shared interrupts", ESP_FAIL);
UART_ENTER_CRITICAL(&uart_spinlock[uart_num]);
switch(uart_num) {
case UART_NUM_1:

View file

@ -47,6 +47,7 @@ extern "C" {
#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.
@ -79,8 +80,8 @@ extern "C" {
typedef void (*intr_handler_t)(void *arg);
typedef struct int_handle_data_t int_handle_data_t;
typedef int_handle_data_t* int_handle_t ;
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
@ -134,7 +135,7 @@ esp_err_t esp_intr_reserve(int intno, int cpu);
* @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 int_handle_t to store a handle that can later be
* @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.
*
@ -142,7 +143,7 @@ esp_err_t esp_intr_reserve(int intno, int cpu);
* 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, int_handle_t *ret_handle);
esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *arg, intr_handle_t *ret_handle);
/**
@ -171,7 +172,7 @@ esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *ar
* @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 int_handle_t to store a handle that can later be
* @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.
*
@ -179,7 +180,7 @@ esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *ar
* 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, int_handle_t *ret_handle);
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);
/**
@ -194,7 +195,7 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre
* where the interrupt is allocated on.
* ESP_OK otherwise
*/
esp_err_t esp_intr_free(int_handle_t handle);
esp_err_t esp_intr_free(intr_handle_t handle);
/**
@ -204,7 +205,7 @@ esp_err_t esp_intr_free(int_handle_t handle);
*
* @return The core number where the interrupt is allocated
*/
int esp_intr_get_cpu(int_handle_t handle);
int esp_intr_get_cpu(intr_handle_t handle);
/**
* @brief Get the allocated interrupt for a certain handle
@ -213,32 +214,36 @@ int esp_intr_get_cpu(int_handle_t handle);
*
* @return The interrupt number
*/
int esp_intr_get_intno(int_handle_t handle);
int esp_intr_get_intno(intr_handle_t handle);
/**
* @brief Disable the interrupt associated with the handle
*
* @note This function can only disable non-shared inteerupts allocated on the CPU that runs this function.
* @warning Do not call this in a critical section; when the critical section ends the interrupt status
* on critical section enter may be restored.
*
* @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(int_handle_t handle);
esp_err_t esp_intr_disable(intr_handle_t handle);
/**
* @brief Ensable the interrupt associated with the handle
*
* @note This function can only enable non-shared inteerupts allocated on the CPU that runs this function.
* @warning Do not call this in a critical section; when the critical section ends the interrupt status
* on critical section enter may be restored.
*
* @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(int_handle_t handle);
esp_err_t esp_intr_enable(intr_handle_t handle);
/**

View file

@ -131,28 +131,12 @@ const static int_desc_t int_desc[32]={
{ 5, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //31
};
//For memory usage and to get an unique ID for every int on every CPU core, the
//intrs and cpus are stored in in one int. These functions handle that.
inline static int to_intno_cpu(int intno, int cpu)
{
return intno+cpu*32;
}
inline static int to_intno(int intno_cpu)
{
return (intno_cpu)&31;
}
inline static int to_cpu(int intno_cpu)
{
return (intno_cpu)/32;
}
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;
@ -166,20 +150,23 @@ struct shared_vector_desc_t {
#define VECDESC_FL_SHARED (1<<2)
#define VECDESC_FL_NONSHARED (1<<3)
//Pack using bitfields for better memory use
struct vector_desc_t {
int intno_cpu; //intno+cpu*32
int flags; //OR of VECDESC_FLAG_* defines
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 int_handle_data_t {
struct intr_handle_data_t {
vector_desc_t *vector_desc;
shared_vector_desc_t *shared_vector_desc;
};
//Linked list of vector descriptions, sorted by intno_cpu value
//Linked list of vector descriptions, sorted by cpu.intno value
static vector_desc_t *vector_desc_head;
//This bitmask has an 1 if the int should be disabled when the flash is disabled.
@ -192,13 +179,14 @@ static bool non_iram_int_disabled_flag[portNUM_PROCESSORS];
static portMUX_TYPE spinlock = portMUX_INITIALIZER_UNLOCKED;
//Inserts an item into vector_desc list so that the list is sorted
//with an incrementing intno_cpu value.
//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->intno_cpu >= to_insert->intno_cpu) break;
if (vd->cpu > to_insert->cpu) break;
if (vd->cpu == to_insert->cpu && vd->intno >= to_insert->intno) break;
prev=vd;
vd=vd->next;
}
@ -217,7 +205,7 @@ static vector_desc_t *find_desc_for_int(int intno, int cpu)
{
vector_desc_t *vd=vector_desc_head;
while(vd!=NULL) {
if (vd->intno_cpu==to_intno_cpu(intno, cpu)) break;
if (vd->cpu==cpu && vd->intno==intno) break;
vd=vd->next;
}
return vd;
@ -233,7 +221,8 @@ static vector_desc_t *get_desc_for_int(int intno, int cpu)
vector_desc_t *newvd=malloc(sizeof(vector_desc_t));
if (newvd==NULL) return NULL;
memset(newvd, 0, sizeof(vector_desc_t));
newvd->intno_cpu=to_intno_cpu(intno, cpu);
newvd->intno=intno;
newvd->cpu=cpu;
insert_vector_desc(newvd);
return newvd;
} else {
@ -296,6 +285,7 @@ static bool int_has_handler(int intr, int cpu)
//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_free_int(int flags, int cpu, int force)
{
int x;
@ -323,10 +313,14 @@ static int get_free_int(int flags, int cpu, int force)
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));
//Check if interrupt is not reserved by design
if (int_desc[x].cpuflags[cpu]==INTDESC_RESVD) { //ToDo: Check for SPECIAL and force!=-1
if (int_desc[x].cpuflags[cpu]==INTDESC_RESVD) {
ALCHLOG(TAG, "....Unusable: reserved");
continue;
}
if (int_desc[x].cpuflags[cpu]==INTDESC_SPECIAL && force==-1) {
ALCHLOG(TAG, "....Unusable: special-purpose int");
continue;
}
//Check if the interrupt level is acceptable
if (!(flags&(1<<int_desc[x].level))) {
ALCHLOG(TAG, "....Unusable: incompatible level");
@ -426,10 +420,12 @@ static void IRAM_ATTR shared_intr_isr(void *arg)
shared_vector_desc_t *sh_vec=vd->shared_vec_info;
portENTER_CRITICAL(&spinlock);
while(sh_vec) {
if ((sh_vec->statusreg == NULL) || (*sh_vec->statusreg & sh_vec->statusmask)) {
sh_vec->isr(sh_vec->arg);
sh_vec=sh_vec->next;
if (!sh_vec->disabled) {
if ((sh_vec->statusreg == NULL) || (*sh_vec->statusreg & sh_vec->statusmask)) {
sh_vec->isr(sh_vec->arg);
}
}
sh_vec=sh_vec->next;
}
portEXIT_CRITICAL(&spinlock);
}
@ -437,19 +433,18 @@ static void IRAM_ATTR shared_intr_isr(void *arg)
//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, int_handle_t *ret_handle)
void *arg, intr_handle_t *ret_handle)
{
int_handle_data_t *ret=NULL;
intr_handle_data_t *ret=NULL;
int force=-1;
printf("Src %d reg/mask %x/%x\n", source, intrstatusreg, intrstatusmask);
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
if ((flags&ESP_INTR_FLAG_SHARED) && (!handler)) return ESP_ERR_INVALID_ARG;
//Only shared interrupts can have status reg / mask
if (intrstatusreg && (!(flags&ESP_INTR_FLAG_SHARED))) 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;
@ -472,11 +467,9 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre
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;
//If we should return a handle, allocate it here.
if (ret_handle!=NULL) {
ret=malloc(sizeof(int_handle_data_t));
if (ret==NULL) return ESP_ERR_NO_MEM;
}
//Allocate a return handle. If we end up not needing it, we'll free it later on.
ret=malloc(sizeof(intr_handle_data_t));
if (ret==NULL) return ESP_ERR_NO_MEM;
portENTER_CRITICAL(&spinlock);
int cpu=xPortGetCoreID();
@ -511,6 +504,8 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre
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.
@ -522,6 +517,7 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre
xt_set_interrupt_handler(intr, handler, arg);
}
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;
@ -533,22 +529,34 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre
if (source>=0) {
intr_matrix_set(cpu, source, intr);
}
//Fill return handle if needed
if (ret_handle!=NULL) {
ret->vector_desc=vd;
ret->shared_vector_desc=vd->shared_vec_info;
*ret_handle=ret;
//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);
}
//We enable the interrupt in any case. For shared interrupts, the interrupts are enabled as soon as we exit
//the critical region anyway, so this is consistent.
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);
ESP_INTR_ENABLE(intr);
return ESP_OK;
}
esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *arg, int_handle_t *ret_handle)
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
@ -559,13 +567,14 @@ esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *ar
}
esp_err_t esp_intr_free(int_handle_t handle)
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 (to_cpu(handle->vector_desc->intno_cpu)!=xPortGetCoreID()) return ESP_ERR_INVALID_ARG;
if (handle->vector_desc->cpu!=xPortGetCoreID()) return ESP_ERR_INVALID_ARG;
esp_intr_disable(handle);
portENTER_CRITICAL(&spinlock);
if (handle->vector_desc->flags&VECDESC_FL_SHARED) {
//Find and kill the shared int
@ -593,46 +602,87 @@ esp_err_t esp_intr_free(int_handle_t handle)
if ((handle->vector_desc->flags&VECDESC_FL_NONSHARED) || free_shared_vector) {
ESP_LOGV(TAG, "esp_intr_free: Disabling int, killing handler");
//Interrupt is not shared. Just disable it and revert to the default interrupt handler.
ESP_INTR_DISABLE(to_intno(handle->vector_desc->intno_cpu));
xt_set_interrupt_handler(to_intno(handle->vector_desc->intno_cpu), xt_unhandled_interrupt, NULL);
//Reset to normal handler
xt_set_interrupt_handler(handle->vector_desc->intno, xt_unhandled_interrupt, (void*)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[to_cpu(handle->vector_desc->intno_cpu)]&=~(1<<(to_intno(handle->vector_desc->intno_cpu)));
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(int_handle_t handle)
int esp_intr_get_intno(intr_handle_t handle)
{
return to_intno(handle->vector_desc->intno_cpu);
return handle->vector_desc->intno;
}
int esp_intr_get_cpu(int_handle_t handle)
int esp_intr_get_cpu(intr_handle_t handle)
{
return to_cpu(handle->vector_desc->intno_cpu);
return handle->vector_desc->cpu;
}
esp_err_t esp_intr_enable(int_handle_t handle)
/*
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 esp_intr_enable(intr_handle_t handle)
{
if (!handle) return ESP_ERR_INVALID_ARG;
if (handle->shared_vector_desc) return ESP_ERR_INVALID_ARG; //Shared ints can't be enabled using this function.
if (to_cpu(handle->vector_desc->intno_cpu)!=xPortGetCoreID()) return ESP_ERR_INVALID_ARG; //Can only enable ints on this cpu
ESP_INTR_ENABLE(to_intno(handle->vector_desc->intno_cpu));
portENTER_CRITICAL(&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(&spinlock);
return ESP_OK;
}
esp_err_t esp_intr_disable(int_handle_t handle)
esp_err_t esp_intr_disable(intr_handle_t handle)
{
if (!handle) return ESP_ERR_INVALID_ARG;
if (handle->shared_vector_desc) return ESP_ERR_INVALID_ARG; //Shared ints can't be disabled using this function.
if (to_cpu(handle->vector_desc->intno_cpu)!=xPortGetCoreID()) return ESP_ERR_INVALID_ARG; //Can only disable ints on this cpu
ESP_INTR_DISABLE(to_intno(handle->vector_desc->intno_cpu));
portENTER_CRITICAL(&spinlock);
int source;
if (handle->shared_vector_desc) {
handle->shared_vector_desc->disabled=1;
source=handle->shared_vector_desc->source;
} else {
source=handle->vector_desc->source;
}
if (source >= 0) {
//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(&spinlock);
return ESP_ERR_INVALID_ARG; //Can only enable these ints on this cpu
}
ESP_INTR_DISABLE(handle->vector_desc->intno);
}
portEXIT_CRITICAL(&spinlock);
return ESP_OK;
}

View file

@ -0,0 +1,150 @@
/*
Test for multicore FreeRTOS. This test spins up threads, fiddles with queues etc.
*/
#include <esp_types.h>
#include <stdio.h>
#include "rom/ets_sys.h"
#include "freertos/FreeRTOS.h"
#include "freertos/task.h"
#include "freertos/semphr.h"
#include "freertos/queue.h"
#include "freertos/xtensa_api.h"
#include "unity.h"
#include "soc/uart_reg.h"
#include "soc/dport_reg.h"
#include "soc/io_mux_reg.h"
#include "esp_intr_alloc.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 */
/*
* @brief timer group0 hardware timer1 init
*/
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) {
TIMERG0.int_clr_timers.t0 = 1;
TIMERG0.hw_timer[0].update=1;
TIMERG0.hw_timer[0].config.alarm_en = 1;
}
if (timer_idx==1) {
TIMERG0.int_clr_timers.t1 = 1;
TIMERG0.hw_timer[1].update=1;
TIMERG0.hw_timer[1].config.alarm_en = 1;
}
if (timer_idx==2) {
TIMERG1.int_clr_timers.t0 = 1;
TIMERG1.hw_timer[0].update=1;
TIMERG1.hw_timer[0].config.alarm_en = 1;
}
if (timer_idx==3) {
TIMERG1.int_clr_timers.t1 = 1;
TIMERG1.hw_timer[1].update=1;
TIMERG1.hw_timer[1].config.alarm_en = 1;
}
// ets_printf("int %d\n", timer_idx);
}
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_RATE_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_RATE_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_RATE_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]);
}
TEST_CASE("Intr_alloc test, private ints", "[esp32]")
{
timer_test(0);
}
TEST_CASE("Intr_alloc test, shared ints", "[esp32]")
{
timer_test(ESP_INTR_FLAG_SHARED);
}

View file

@ -74,6 +74,7 @@ CONFIG_OPTIMIZATION_LEVEL_DEBUG=y
#
# Component config
#
CONFIG_BTC_TASK_STACK_SIZE=2048
CONFIG_BT_RESERVE_DRAM=0
#
@ -85,7 +86,6 @@ CONFIG_ESP32_DEFAULT_CPU_FREQ_240=y
CONFIG_ESP32_DEFAULT_CPU_FREQ_MHZ=240
# CONFIG_ESP32_ENABLE_STACK_WIFI is not set
# CONFIG_ESP32_ENABLE_STACK_BT is not set
CONFIG_ESP32_ENABLE_STACK_NONE=y
CONFIG_MEMMAP_SMP=y
# CONFIG_MEMMAP_TRACEMEM is not set
CONFIG_TRACEMEM_RESERVE_DRAM=0x0
@ -165,7 +165,6 @@ CONFIG_MBEDTLS_SSL_MAX_CONTENT_LEN=16384
CONFIG_MBEDTLS_HARDWARE_AES=y
CONFIG_MBEDTLS_HARDWARE_MPI=y
CONFIG_MBEDTLS_MPI_USE_INTERRUPT=y
CONFIG_MBEDTLS_MPI_INTERRUPT_NUM=18
CONFIG_MBEDTLS_HARDWARE_SHA=y
#