From ae8c37e0b6c64725e045298cf890ce6d9fa0aaa0 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Wed, 7 Dec 2016 21:30:21 +0800 Subject: [PATCH] Add disabling/enabling of shared interrupt, add testcase for interrupts --- components/driver/gpio.c | 4 +- components/driver/include/driver/gpio.h | 6 +- components/driver/include/driver/ledc.h | 4 +- components/driver/include/driver/pcnt.h | 5 +- components/driver/include/driver/rmt.h | 16 +- components/driver/include/driver/timer.h | 10 +- components/driver/include/driver/uart.h | 3 +- components/driver/ledc.c | 4 +- components/driver/pcnt.c | 4 +- components/driver/rmt.c | 15 +- components/driver/timer.c | 10 +- components/driver/uart.c | 3 +- components/esp32/include/esp_intr_alloc.h | 27 ++-- components/esp32/intr_alloc.c | 184 ++++++++++++++-------- components/esp32/test/test_intr_alloc.c | 150 ++++++++++++++++++ tools/unit-test-app/sdkconfig | 3 +- 16 files changed, 347 insertions(+), 101 deletions(-) create mode 100644 components/esp32/test/test_intr_alloc.c diff --git a/components/driver/gpio.c b/components/driver/gpio.c index 2cba63ca6f..a78239eae7 100644 --- a/components/driver/gpio.c +++ b/components/driver/gpio.c @@ -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*/ diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index 31d54d0993..7f178677bc 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -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); diff --git a/components/driver/include/driver/ledc.h b/components/driver/include/driver/ledc.h index 3042aa2518..6430523252 100644 --- a/components/driver/include/driver/ledc.h +++ b/components/driver/include/driver/ledc.h @@ -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 diff --git a/components/driver/include/driver/pcnt.h b/components/driver/include/driver/pcnt.h index cab324936c..d852f8fe79 100644 --- a/components/driver/include/driver/pcnt.h +++ b/components/driver/include/driver/pcnt.h @@ -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 diff --git a/components/driver/include/driver/rmt.h b/components/driver/include/driver/rmt.h index 9e6882efbd..1c136689b7 100644 --- a/components/driver/include/driver/rmt.h +++ b/components/driver/include/driver/rmt.h @@ -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. diff --git a/components/driver/include/driver/timer.h b/components/driver/include/driver/timer.h index e6cbbd3787..f46e708734 100644 --- a/components/driver/include/driver/timer.h +++ b/components/driver/include/driver/timer.h @@ -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. * diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index 0e7a4b515b..be3a6c8fc6 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -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 diff --git a/components/driver/ledc.c b/components/driver/ledc.c index 66c31da9b9..8d7ef89e7b 100644 --- a/components/driver/ledc.c +++ b/components/driver/ledc.c @@ -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; } diff --git a/components/driver/pcnt.c b/components/driver/pcnt.c index aedd22104a..9618d1599e 100644 --- a/components/driver/pcnt.c +++ b/components/driver/pcnt.c @@ -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); } diff --git a/components/driver/rmt.c b/components/driver/rmt.c index ba70ffea60..8f3816ec9a 100644 --- a/components/driver/rmt.c +++ b/components/driver/rmt.c @@ -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); diff --git a/components/driver/timer.c b/components/driver/timer.c index 023f731cc3..8572753794 100644 --- a/components/driver/timer.c +++ b/components/driver/timer.c @@ -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<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); /** diff --git a/components/esp32/intr_alloc.c b/components/esp32/intr_alloc.c index a13424af8c..9eec38c42f 100644 --- a/components/esp32/intr_alloc.c +++ b/components/esp32/intr_alloc.c @@ -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<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 + + //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->vector_desc=vd; - ret->shared_vector_desc=vd->shared_vec_info; *ret_handle=ret; + } else { + free(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); 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; } diff --git a/components/esp32/test/test_intr_alloc.c b/components/esp32/test/test_intr_alloc.c new file mode 100644 index 0000000000..30f7d4bab9 --- /dev/null +++ b/components/esp32/test/test_intr_alloc.c @@ -0,0 +1,150 @@ +/* + Test for multicore FreeRTOS. This test spins up threads, fiddles with queues etc. +*/ + +#include +#include +#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); +} diff --git a/tools/unit-test-app/sdkconfig b/tools/unit-test-app/sdkconfig index d82f83561d..9e5be636de 100644 --- a/tools/unit-test-app/sdkconfig +++ b/tools/unit-test-app/sdkconfig @@ -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 # -- 2.40.0