From 655fd2986a85149afcaad67553624e9b1872bab4 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Fri, 25 Nov 2016 17:33:51 +0800 Subject: [PATCH 1/7] Add interrupt allocation scheme / interrupt sharing. Also modifies drivers and examples. Also allows interrupts to be marked specifically as having a handler that's all in IRAM. --- components/driver/gpio.c | 9 +- components/driver/include/driver/gpio.h | 12 +- components/driver/include/driver/ledc.h | 17 +- components/driver/include/driver/pcnt.h | 8 +- components/driver/include/driver/rmt.h | 22 +- components/driver/include/driver/timer.h | 12 +- components/driver/include/driver/uart.h | 17 +- components/driver/ledc.c | 11 +- components/driver/pcnt.c | 9 +- components/driver/rmt.c | 17 +- components/driver/timer.c | 15 +- components/driver/uart.c | 38 +- components/esp32/cpu_start.c | 12 +- components/esp32/crosscore_int.c | 8 +- components/esp32/include/esp_intr_alloc.h | 262 +++++++ components/esp32/intr_alloc.c | 654 ++++++++++++++++++ components/esp32/task_wdt.c | 18 +- components/ethernet/emac_main.c | 8 +- components/freertos/xtensa_intr.c | 1 - components/freertos/xtensa_intr_asm.S | 29 +- components/mbedtls/port/esp_bignum.c | 6 +- components/newlib/time.c | 5 +- components/spi_flash/cache_utils.c | 11 + docs/Doxyfile | 3 +- docs/api/intr_alloc.rst | 84 +++ docs/index.rst | 1 + examples/11_rmt_nec_tx_rx/main/infrared_nec.c | 5 +- examples/13_timer_group/main/timer_group.c | 6 +- examples/16_pcnt/main/pcnt_test.c | 3 +- 29 files changed, 1142 insertions(+), 161 deletions(-) create mode 100644 components/esp32/include/esp_intr_alloc.h create mode 100644 components/esp32/intr_alloc.c create mode 100644 docs/api/intr_alloc.rst diff --git a/components/driver/gpio.c b/components/driver/gpio.c index f8694597a8..2cba63ca6f 100644 --- a/components/driver/gpio.c +++ b/components/driver/gpio.c @@ -14,6 +14,7 @@ #include #include "esp_err.h" #include "esp_intr.h" +#include "esp_intr_alloc.h" #include "freertos/FreeRTOS.h" #include "freertos/xtensa_api.h" #include "driver/gpio.h" @@ -320,14 +321,10 @@ esp_err_t gpio_config(gpio_config_t *pGPIOConfig) return ESP_OK; } -esp_err_t gpio_isr_register(uint32_t gpio_intr_num, void (*fn)(void*), void * arg) +esp_err_t gpio_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags) { GPIO_CHECK(fn, "GPIO ISR null", ESP_ERR_INVALID_ARG); - ESP_INTR_DISABLE(gpio_intr_num); - intr_matrix_set(xPortGetCoreID(), ETS_GPIO_INTR_SOURCE, gpio_intr_num); - xt_set_interrupt_handler(gpio_intr_num, fn, arg); - ESP_INTR_ENABLE(gpio_intr_num); - return ESP_OK; + return esp_intr_alloc(ETS_GPIO_INTR_SOURCE, intr_alloc_flags, fn, arg, NULL); } /*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 de7525bd5f..59dd568578 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -343,8 +343,9 @@ esp_err_t gpio_wakeup_disable(gpio_num_t gpio_num); * Users should know that which CPU is running and then pick a INUM that is not used by system. * We can find the information of INUM and interrupt level in soc.h. * - * @param gpio_intr_num GPIO interrupt number,check the info in soc.h, and please see the core-isa.h for more details * @param fn Interrupt 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 * Note that the handler function MUST be defined with attribution of "IRAM_ATTR". @@ -355,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(uint32_t gpio_intr_num, void (*fn)(void*), void * arg); +esp_err_t gpio_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags); @@ -415,7 +416,7 @@ esp_err_t gpio_pulldown_dis(gpio_num_t gpio_num); */ /** - *----------EXAMPLE TO CONIFGURE GPIO AS OUTPUT ------------ * + *----------EXAMPLE TO CONFIGURE GPIO AS OUTPUT ------------ * * @code{c} * gpio_config_t io_conf; * io_conf.intr_type = GPIO_INTR_DISABLE; //disable interrupt @@ -428,7 +429,7 @@ esp_err_t gpio_pulldown_dis(gpio_num_t gpio_num); **/ /** - *----------EXAMPLE TO CONIFGURE GPIO AS OUTPUT ------------ * + *----------EXAMPLE TO CONFIGURE GPIO AS OUTPUT ------------ * * @code{c} * io_conf.intr_type = GPIO_INTR_POSEDGE; //set posedge interrupt * io_conf.mode = GPIO_MODE_INPUT; //set as input @@ -441,8 +442,7 @@ esp_err_t gpio_pulldown_dis(gpio_num_t gpio_num); /** *----------EXAMPLE TO SET ISR HANDLER ---------------------- * @code{c} - * //the first parameter is INUM, you can pick one form interrupt level 1/2 which is not used by the system. - * gpio_isr_register(18,gpio_intr_test,NULL); //hook the isr handler for GPIO interrupt + * gpio_isr_register(gpio_intr_test,NULL, 0); //hook the isr handler for GPIO interrupt * @endcode * @note * 1. user should arrange the INUMs that used, better not to use a same INUM for different interrupt. diff --git a/components/driver/include/driver/ledc.h b/components/driver/include/driver/ledc.h index e07787b2b1..a581f604ec 100644 --- a/components/driver/include/driver/ledc.h +++ b/components/driver/include/driver/ledc.h @@ -257,11 +257,11 @@ esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty, /** * @brief register LEDC interrupt handler, the handler is an ISR. * The handler will be attached to the same CPU core that this function is running on. - * @note - * Users should know that which CPU is running and then pick a INUM that is not used by system. - * We can find the information of INUM and interrupt level in soc.h. - * @param ledc_intr_num LEDC interrupt number, check the info in soc.h, and please see the core-isa.h for more details + * * @param fn Interrupt handler function. + * @param arg User-supplied argument passed to the 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 * Note that the handler function MUST be defined with attribution of "IRAM_ATTR". * @param arg Parameter for handler function @@ -270,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(uint32_t ledc_intr_num, void (*fn)(void*), void * arg); +esp_err_t ledc_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags); /** * @brief configure LEDC settings @@ -398,13 +398,8 @@ esp_err_t ledc_bind_channel_timer(ledc_mode_t speed_mode, uint32_t channel, uint * ----------------EXAMPLE OF LEDC INTERRUPT ------------------ * @code{c} * //we have fade_end interrupt and counter overflow interrupt. we just give an example of fade_end interrupt here. - * ledc_isr_register(18, ledc_isr_handler, NULL); //hook the isr handler for LEDC interrupt + * ledc_isr_register(ledc_isr_handler, NULL, 0); //hook the isr handler for LEDC interrupt * @endcode - * @note - * 1. the first parameter is INUM, you can pick one form interrupt level 1/2 which is not used by the system. - * 2. user should arrange the INUMs that used, better not to use a same INUM for different interrupt source. - * 3. do not pick the INUM that already occupied by the system. - * 4. refer to soc.h to check which INUMs that can be used. * * ----------------EXAMPLE OF INTERRUPT HANDLER --------------- * @code{c} diff --git a/components/driver/include/driver/pcnt.h b/components/driver/include/driver/pcnt.h index d2620cf15e..1ab2abae74 100644 --- a/components/driver/include/driver/pcnt.h +++ b/components/driver/include/driver/pcnt.h @@ -213,21 +213,19 @@ esp_err_t pcnt_get_event_value(pcnt_unit_t unit, pcnt_evt_type_t evt_type, int16 /** * @brief Register PCNT interrupt handler, the handler is an ISR. * The handler will be attached to the same CPU core that this function is running on. - * @note - * Users should know that which CPU is running and then pick a INUM that is not used by system. - * We can find the information of INUM and interrupt level in soc.h. * - * @param pcnt_intr_num PCNT interrupt number, check the info in soc.h, and please see the core-isa.h for more details * @param fn Interrupt handler function. * @note * Note that the handler function MUST be defined with attribution of "IRAM_ATTR". * @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. * * @return * - ESP_OK Success * - ESP_ERR_INVALID_ARG Function pointer error. */ -esp_err_t pcnt_isr_register(uint32_t pcnt_intr_num, void (*fn)(void*), void * arg); +esp_err_t pcnt_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags); /** * @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 50a5c743dd..4c28406d80 100644 --- a/components/driver/include/driver/rmt.h +++ b/components/driver/include/driver/rmt.h @@ -566,27 +566,21 @@ esp_err_t rmt_config(rmt_config_t* rmt_param); * @brief register RMT interrupt handler, the handler is an ISR. * * The handler will be attached to the same CPU core that this function is running on. - * Users should know that which CPU is running and then pick a INUM that is not used by system. - * We can find the information of INUM and interrupt level in soc.h. * @note * If you already called rmt_driver_install to use system RMT driver, * please do not register ISR handler again. * - * @param rmt_intr_num RMT interrupt number, check the info in soc.h, and please see the core-isa.h for more details - * * @param fn Interrupt handler function. - * - * @note - * the handler function MUST be defined with attribution of "IRAM_ATTR". - * * @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. * * @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(uint8_t rmt_intr_num, void (* fn)(void* ), void * arg); +esp_err_t rmt_isr_register(void (* fn)(void* ), void * arg, int intr_alloc_flags); /** * @brief Fill memory data of channel with given RMT items. @@ -727,7 +721,7 @@ esp_err_t rmt_get_ringbuf_handler(rmt_channel_t channel, RingbufHandle_t* buf_ha * rmt_config(&rmt_tx); * * //install system RMT driver, disable rx ringbuffer for transmitter. - * rmt_driver_install(rmt_tx.channel, 0, RMT_INTR_NUM); + * rmt_driver_install(rmt_tx.channel, 0, 0); * } * * @endcode @@ -747,20 +741,16 @@ esp_err_t rmt_get_ringbuf_handler(rmt_channel_t channel, RingbufHandle_t* buf_ha * rmt_config(&rmt_rx); * * //install system RMT driver. - * rmt_driver_install(rmt_rx.channel, 1000, RMT_INTR_NUM); + * rmt_driver_install(rmt_rx.channel, 1000, 0); * } * * ----------------EXAMPLE OF RMT INTERRUPT ------------------ * @code{c} * - * rmt_isr_register(RMT_INTR_NUM, rmt_isr, NULL); //hook the ISR handler for RMT interrupt + * rmt_isr_register(rmt_isr, NULL, 0); //hook the ISR handler for RMT interrupt * @endcode * @note * 0. If you have called rmt_driver_install, you don't need to set ISR handler any more. - * 1. the first parameter is INUM, you can pick one form interrupt level 1/2 which is not used by the system. - * 2. user should arrange the INUMs that used, better not to use a same INUM for different interrupt source. - * 3. do not pick the INUM that already occupied by the system. - * 4. refer to soc.h to check which INUMs that can be used. * * ----------------EXAMPLE OF INTERRUPT HANDLER --------------- * @code{c} diff --git a/components/driver/include/driver/timer.h b/components/driver/include/driver/timer.h index c0ad7116e4..e6cbbd3787 100644 --- a/components/driver/include/driver/timer.h +++ b/components/driver/include/driver/timer.h @@ -94,8 +94,8 @@ typedef enum { typedef struct { bool alarm_en; /*!< Timer alarm enable */ bool counter_en; /*!< Counter enable */ - timer_count_dir_t counter_dir; /*!< Counter direction */ timer_intr_mode_t intr_type; /*!< Interrupt mode */ + timer_count_dir_t counter_dir; /*!< Counter direction */ bool auto_reload; /*!< Timer auto-reload */ uint16_t divider; /*!< Counter clock divider*/ } timer_config_t; @@ -245,21 +245,17 @@ esp_err_t timer_set_alarm(timer_group_t group_num, timer_idx_t timer_num, timer_ /** * @brief register Timer interrupt handler, the handler is an ISR. * The handler will be attached to the same CPU core that this function is running on. - * @note - * Users should know that which CPU is running and then pick a INUM that is not used by system. - * We can find the information of INUM and interrupt level in soc.h. * * @param group_num Timer group number * @param timer_num Timer index of timer group - * @param timer_intr_num TIMER interrupt number, check the info in soc.h, and please see the core-isa.h for more details - * @param intr_type Timer interrupt type * @param fn Interrupt handler function. * @note * Code inside the handler function can only call functions in IRAM, so cannot call other timer APIs. * Use direct register access to access timers from inside the ISR. * * @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. * @return * - ESP_OK Success * - ESP_ERR_INVALID_ARG Function pointer error. @@ -268,7 +264,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, int timer_intr_num, timer_intr_mode_t intr_type, void (*fn)(void*), void * arg); +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); /** @brief Initializes and configure the timer. * diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index 1ddfcad9fa..0e7a4b515b 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -366,21 +366,19 @@ esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh); * @brief register UART interrupt handler(ISR). * * @note UART ISR handler will be attached to the same CPU core that this function is running on. - * Users should know that which CPU is running and then pick a INUM that is not used by system. - * We can find the information of INUM and interrupt level in soc.h. - * - * @attention The ISR handler function MUST be defined with attribution of "IRAM_ATTR" for now. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 - * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details * @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. * * @return * - ESP_OK Success * - ESP_FAIL Parameter error */ -esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (*fn)(void*), void * arg); +esp_err_t uart_isr_register(uart_port_t uart_num, void (*fn)(void*), void * arg, int intr_alloc_flags); /** * @brief Set UART pin number @@ -461,14 +459,15 @@ esp_err_t uart_intr_config(uart_port_t uart_num, const uart_intr_config_t *intr_ * @param tx_buffer_size UART TX ring buffer size. * If set to zero, driver will not use TX buffer, TX function will block task until all data have been sent out.. * @param queue_size UART event queue size/depth. - * @param uart_intr_num UART interrupt number,check the info in soc.h, and please refer to core-isa.h for more details * @param uart_queue UART event queue handle, if set NULL, driver will not use an event queue. + * @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. * * @return * - ESP_OK Success * - ESP_FAIL Parameter error */ -esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue); +esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, void* uart_queue, int intr_alloc_flags); /** * @brief Uninstall UART driver. @@ -733,7 +732,7 @@ esp_err_t uart_flush(uart_port_t uart_num); * //Set UART log level * esp_log_level_set(TAG, ESP_LOG_INFO); * //Install UART driver, and get the queue. - * uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, 17, &uart0_queue); + * uart_driver_install(uart_num, 1024 * 2, 1024*4, 10, &uart0_queue, 0); * //Create a task to handler UART event from ISR * xTaskCreate(uart_task, "uTask", 1024, (void*)uart_num, 10, NULL); * } diff --git a/components/driver/ledc.c b/components/driver/ledc.c index 893c78a6be..66c31da9b9 100644 --- a/components/driver/ledc.c +++ b/components/driver/ledc.c @@ -13,6 +13,7 @@ // limitations under the License. #include #include "esp_intr.h" +#include "esp_intr_alloc.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" #include "freertos/xtensa_api.h" @@ -113,16 +114,14 @@ 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(uint32_t ledc_intr_num, void (*fn)(void*), void * arg) +esp_err_t ledc_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags) { + esp_err_t ret; LEDC_CHECK(fn, "ledc isr null", ESP_ERR_INVALID_ARG); portENTER_CRITICAL(&ledc_spinlock); - ESP_INTR_DISABLE(ledc_intr_num); - intr_matrix_set(xPortGetCoreID(), ETS_LEDC_INTR_SOURCE, ledc_intr_num); - xt_set_interrupt_handler(ledc_intr_num, fn, arg); - ESP_INTR_ENABLE(ledc_intr_num); + ret=esp_intr_alloc(ETS_LEDC_INTR_SOURCE, intr_alloc_flags, fn, arg, NULL); portEXIT_CRITICAL(&ledc_spinlock); - return ESP_OK; + return ret; } esp_err_t ledc_timer_config(ledc_timer_config_t* timer_conf) diff --git a/components/driver/pcnt.c b/components/driver/pcnt.c index e65d733c01..aedd22104a 100644 --- a/components/driver/pcnt.c +++ b/components/driver/pcnt.c @@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. #include "esp_log.h" +#include "esp_intr_alloc.h" #include "driver/pcnt.h" #include "driver/periph_ctrl.h" @@ -266,13 +267,9 @@ esp_err_t pcnt_filter_disable(pcnt_unit_t unit) return ESP_OK; } -esp_err_t pcnt_isr_register(uint32_t pcnt_intr_num, void (*fun)(void*), void * arg) +esp_err_t pcnt_isr_register(void (*fun)(void*), void * arg, int intr_alloc_flags) { PCNT_CHECK(fun != NULL, PCNT_ADDRESS_ERR_STR, ESP_ERR_INVALID_ARG); - ESP_INTR_DISABLE(pcnt_intr_num); - intr_matrix_set(xPortGetCoreID(), ETS_PCNT_INTR_SOURCE, pcnt_intr_num); - xt_set_interrupt_handler(pcnt_intr_num, fun, arg); - ESP_INTR_ENABLE(pcnt_intr_num); - return ESP_OK; + return esp_intr_alloc(ETS_PCNT_INTR_SOURCE, intr_alloc_flags, fun, arg, NULL); } diff --git a/components/driver/rmt.c b/components/driver/rmt.c index f23fa257d0..ba70ffea60 100644 --- a/components/driver/rmt.c +++ b/components/driver/rmt.c @@ -21,6 +21,7 @@ #include "esp_intr.h" #include "esp_log.h" #include "esp_err.h" +#include "esp_intr_alloc.h" #include "soc/gpio_sig_map.h" #include "soc/rmt_struct.h" #include "driver/periph_ctrl.h" @@ -472,17 +473,15 @@ 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(uint8_t rmt_intr_num, void (*fn)(void*), void * arg) +esp_err_t rmt_isr_register(void (*fn)(void*), void * arg, int intr_alloc_flags) { + 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); - ESP_INTR_DISABLE(rmt_intr_num); - intr_matrix_set(xPortGetCoreID(), ETS_RMT_INTR_SOURCE, rmt_intr_num); - xt_set_interrupt_handler(rmt_intr_num, fn, arg); - ESP_INTR_ENABLE(rmt_intr_num); + ret=esp_intr_alloc(ETS_RMT_INTR_SOURCE, intr_alloc_flags, fn, arg, NULL); portEXIT_CRITICAL(&rmt_spinlock); - return ESP_OK; + return ret; } static int IRAM_ATTR rmt_get_mem_len(rmt_channel_t channel) @@ -619,7 +618,7 @@ esp_err_t rmt_driver_uninstall(rmt_channel_t channel) return ESP_OK; } -esp_err_t rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int rmt_intr_num) +esp_err_t rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int intr_alloc_flags) { RMT_CHECK(channel < RMT_CHANNEL_MAX, RMT_CHANNEL_ERROR_STR, ESP_ERR_INVALID_ARG); if(p_rmt_obj[channel] != NULL) { @@ -627,7 +626,6 @@ esp_err_t rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int rmt_ return ESP_FAIL; } - ESP_INTR_DISABLE(rmt_intr_num); p_rmt_obj[channel] = (rmt_obj_t*) malloc(sizeof(rmt_obj_t)); if(p_rmt_obj[channel] == NULL) { @@ -652,11 +650,10 @@ esp_err_t rmt_driver_install(rmt_channel_t channel, size_t rx_buf_size, int rmt_ rmt_set_err_intr_en(channel, 1); } if(s_rmt_driver_installed == false) { - rmt_isr_register(rmt_intr_num, rmt_driver_isr_default, NULL); + rmt_isr_register(rmt_driver_isr_default, NULL, intr_alloc_flags); s_rmt_driver_installed = true; } rmt_set_tx_intr_en(channel, 1); - ESP_INTR_ENABLE(rmt_intr_num); return ESP_OK; } diff --git a/components/driver/timer.c b/components/driver/timer.c index b305a41468..023f731cc3 100644 --- a/components/driver/timer.c +++ b/components/driver/timer.c @@ -15,6 +15,7 @@ #include "esp_log.h" #include "esp_err.h" #include "esp_intr.h" +#include "esp_intr_alloc.h" #include "freertos/FreeRTOS.h" #include "freertos/xtensa_api.h" #include "driver/timer.h" @@ -167,36 +168,32 @@ esp_err_t timer_set_alarm(timer_group_t group_num, timer_idx_t timer_num, timer_ return ESP_OK; } -esp_err_t timer_isr_register(timer_group_t group_num, timer_idx_t timer_num, int timer_intr_num, - timer_intr_mode_t intr_type, void (*fn)(void*), void * arg) +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_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); - ESP_INTR_DISABLE(timer_intr_num); int intr_source = 0; switch(group_num) { case TIMER_GROUP_0: default: - if(intr_type == TIMER_INTR_LEVEL) { + if((intr_alloc_flags & ESP_INTR_FLAG_EDGE) == 0) { intr_source = ETS_TG0_T0_LEVEL_INTR_SOURCE + timer_num; } else { intr_source = ETS_TG0_T0_EDGE_INTR_SOURCE + timer_num; } break; case TIMER_GROUP_1: - if(intr_type == TIMER_INTR_LEVEL) { + if((intr_alloc_flags & ESP_INTR_FLAG_EDGE) == 0) { intr_source = ETS_TG1_T0_LEVEL_INTR_SOURCE + timer_num; } else { intr_source = ETS_TG1_T0_EDGE_INTR_SOURCE + timer_num; } break; } - intr_matrix_set(xPortGetCoreID(), intr_source, timer_intr_num); - xt_set_interrupt_handler(timer_intr_num, fn, arg); - ESP_INTR_ENABLE(timer_intr_num); - return ESP_OK; + return esp_intr_alloc(intr_source, intr_alloc_flags, fn, arg, NULL); } esp_err_t timer_init(timer_group_t group_num, timer_idx_t timer_num, timer_config_t *config) diff --git a/components/driver/uart.c b/components/driver/uart.c index 59b4904dcd..62979dd5a3 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -15,7 +15,9 @@ #include "esp_types.h" #include "esp_attr.h" #include "esp_intr.h" +#include "esp_intr_alloc.h" #include "esp_log.h" +#include "esp_err.h" #include "malloc.h" #include "freertos/FreeRTOS.h" #include "freertos/semphr.h" @@ -53,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 intr_num; /*!< UART interrupt number*/ + int_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 */ @@ -283,31 +285,29 @@ esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh) UART[uart_num]->conf1.txfifo_empty_thrhd = thresh & UART_TXFIFO_EMPTY_THRHD_V; UART[uart_num]->int_ena.txfifo_empty = enable & 0x1; UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); - ESP_INTR_ENABLE(p_uart_obj[uart_num]->intr_num); return ESP_OK; } -esp_err_t uart_isr_register(uart_port_t uart_num, uint8_t uart_intr_num, void (*fn)(void*), void * arg) +esp_err_t uart_isr_register(uart_port_t uart_num, void (*fn)(void*), void * arg, int intr_alloc_flags) { + 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]); - ESP_INTR_DISABLE(uart_intr_num); switch(uart_num) { case UART_NUM_1: - intr_matrix_set(xPortGetCoreID(), ETS_UART1_INTR_SOURCE, uart_intr_num); + ret=esp_intr_alloc(ETS_UART1_INTR_SOURCE, intr_alloc_flags, fn, arg, &p_uart_obj[uart_num]->intr_handle); break; case UART_NUM_2: - intr_matrix_set(xPortGetCoreID(), ETS_UART2_INTR_SOURCE, uart_intr_num); + ret=esp_intr_alloc(ETS_UART2_INTR_SOURCE, intr_alloc_flags, fn, arg, &p_uart_obj[uart_num]->intr_handle); break; case UART_NUM_0: default: - intr_matrix_set(xPortGetCoreID(), ETS_UART0_INTR_SOURCE, uart_intr_num); + ret=esp_intr_alloc(ETS_UART0_INTR_SOURCE, intr_alloc_flags, fn, arg, &p_uart_obj[uart_num]->intr_handle); break; } - xt_set_interrupt_handler(uart_intr_num, fn, arg); - ESP_INTR_ENABLE(uart_intr_num); UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); - return ESP_OK; + return ret; } //internal signal can be output to multiple GPIO pads @@ -859,7 +859,7 @@ esp_err_t uart_flush(uart_port_t uart_num) //rx sem protect the ring buffer read related functions xSemaphoreTake(p_uart->rx_mux, (portTickType)portMAX_DELAY); - ESP_INTR_DISABLE(p_uart->intr_num); + esp_intr_disable(p_uart->intr_handle); while(true) { if(p_uart->rx_head_ptr) { vRingbufferReturnItem(p_uart->rx_ring_buf, p_uart->rx_head_ptr); @@ -876,12 +876,12 @@ esp_err_t uart_flush(uart_port_t uart_num) p_uart->rx_ptr = NULL; p_uart->rx_cur_remain = 0; p_uart->rx_head_ptr = NULL; - ESP_INTR_ENABLE(p_uart->intr_num); + esp_intr_enable(p_uart->intr_handle); xSemaphoreGive(p_uart->rx_mux); if(p_uart->tx_buf_size > 0) { xSemaphoreTake(p_uart->tx_mux, (portTickType)portMAX_DELAY); - ESP_INTR_DISABLE(p_uart->intr_num); + esp_intr_disable(p_uart->intr_handle); UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); UART[uart_num]->int_ena.txfifo_empty = 0; UART[uart_num]->int_clr.txfifo_empty = 1; @@ -901,19 +901,18 @@ esp_err_t uart_flush(uart_port_t uart_num) p_uart->tx_ptr = NULL; p_uart->tx_waiting_brk = 0; p_uart->tx_waiting_fifo = false; - ESP_INTR_ENABLE(p_uart->intr_num); + esp_intr_enable(p_uart->intr_handle); xSemaphoreGive(p_uart->tx_mux); } uart_reset_fifo(uart_num); return ESP_OK; } -esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, int uart_intr_num, void* uart_queue) +esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_buffer_size, int queue_size, void* uart_queue, int intr_alloc_flags) { UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); UART_CHECK((rx_buffer_size > 0), "uart rx buffer length error", ESP_FAIL); if(p_uart_obj[uart_num] == NULL) { - ESP_INTR_DISABLE(uart_intr_num); p_uart_obj[uart_num] = (uart_obj_t*) malloc(sizeof(uart_obj_t)); if(p_uart_obj[uart_num] == NULL) { ESP_LOGE(UART_TAG, "UART driver malloc error"); @@ -926,7 +925,6 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b p_uart_obj[uart_num]->tx_brk_sem = xSemaphoreCreateBinary(); p_uart_obj[uart_num]->tx_mux = xSemaphoreCreateMutex(); p_uart_obj[uart_num]->rx_mux = xSemaphoreCreateMutex(); - p_uart_obj[uart_num]->intr_num = uart_intr_num; p_uart_obj[uart_num]->queue_size = queue_size; p_uart_obj[uart_num]->tx_ptr = NULL; p_uart_obj[uart_num]->tx_head = NULL; @@ -959,7 +957,7 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b ESP_LOGE(UART_TAG, "UART driver already installed"); return ESP_FAIL; } - uart_isr_register(uart_num, uart_intr_num, uart_rx_intr_handler_default, p_uart_obj[uart_num]); + uart_isr_register(uart_num, uart_rx_intr_handler_default, p_uart_obj[uart_num], intr_alloc_flags); uart_intr_config_t uart_intr = { .intr_enable_mask = UART_RXFIFO_FULL_INT_ENA_M | UART_RXFIFO_TOUT_INT_ENA_M @@ -972,7 +970,6 @@ esp_err_t uart_driver_install(uart_port_t uart_num, int rx_buffer_size, int tx_b .txfifo_empty_intr_thresh = UART_EMPTY_THRESH_DEFAULT }; uart_intr_config(uart_num, &uart_intr); - ESP_INTR_ENABLE(uart_intr_num); return ESP_OK; } @@ -984,10 +981,9 @@ esp_err_t uart_driver_delete(uart_port_t uart_num) ESP_LOGI(UART_TAG, "ALREADY NULL"); return ESP_OK; } - ESP_INTR_DISABLE(p_uart_obj[uart_num]->intr_num); + esp_intr_free(p_uart_obj[uart_num]->intr_handle); uart_disable_rx_intr(uart_num); uart_disable_tx_intr(uart_num); - uart_isr_register(uart_num, p_uart_obj[uart_num]->intr_num, NULL, NULL); if(p_uart_obj[uart_num]->tx_fifo_sem) { vSemaphoreDelete(p_uart_obj[uart_num]->tx_fifo_sem); diff --git a/components/esp32/cpu_start.c b/components/esp32/cpu_start.c index 8802b7dcd7..d6088b0401 100644 --- a/components/esp32/cpu_start.c +++ b/components/esp32/cpu_start.c @@ -173,12 +173,6 @@ void start_cpu0_default(void) uart_div_modify(CONFIG_CONSOLE_UART_NUM, (APB_CLK_FREQ << 4) / CONFIG_CONSOLE_UART_BAUDRATE); #if CONFIG_BROWNOUT_DET esp_brownout_init(); -#endif -#if CONFIG_INT_WDT - esp_int_wdt_init(); -#endif -#if CONFIG_TASK_WDT - esp_task_wdt_init(); #endif esp_setup_time_syscalls(); esp_vfs_dev_uart_register(); @@ -194,6 +188,12 @@ void start_cpu0_default(void) _GLOBAL_REENT->_stderr = (FILE*) &__sf_fake_stderr; #endif do_global_ctors(); +#if CONFIG_INT_WDT + esp_int_wdt_init(); +#endif +#if CONFIG_TASK_WDT + esp_task_wdt_init(); +#endif #if !CONFIG_FREERTOS_UNICORE esp_crosscore_int_init(); #endif diff --git a/components/esp32/crosscore_int.c b/components/esp32/crosscore_int.c index 60f972a2a2..f7ea4f6a74 100644 --- a/components/esp32/crosscore_int.c +++ b/components/esp32/crosscore_int.c @@ -17,6 +17,7 @@ #include "esp_attr.h" #include "esp_err.h" #include "esp_intr.h" +#include "esp_intr_alloc.h" #include "rom/ets_sys.h" #include "rom/uart.h" @@ -72,14 +73,11 @@ void esp_crosscore_int_init() { portENTER_CRITICAL(&reasonSpinlock); reason[xPortGetCoreID()]=0; portEXIT_CRITICAL(&reasonSpinlock); - ESP_INTR_DISABLE(ETS_FROM_CPU_INUM); if (xPortGetCoreID()==0) { - intr_matrix_set(xPortGetCoreID(), ETS_FROM_CPU_INTR0_SOURCE, ETS_FROM_CPU_INUM); + esp_intr_alloc(ETS_FROM_CPU_INTR0_SOURCE, ESP_INTR_FLAG_IRAM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()], NULL); } else { - intr_matrix_set(xPortGetCoreID(), ETS_FROM_CPU_INTR1_SOURCE, ETS_FROM_CPU_INUM); + esp_intr_alloc(ETS_FROM_CPU_INTR1_SOURCE, ESP_INTR_FLAG_IRAM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()], NULL); } - xt_set_interrupt_handler(ETS_FROM_CPU_INUM, esp_crosscore_isr, (void*)&reason[xPortGetCoreID()]); - ESP_INTR_ENABLE(ETS_FROM_CPU_INUM); } void esp_crosscore_int_send_yield(int coreId) { diff --git a/components/esp32/include/esp_intr_alloc.h b/components/esp32/include/esp_intr_alloc.h new file mode 100644 index 0000000000..ea7d597678 --- /dev/null +++ b/components/esp32/include/esp_intr_alloc.h @@ -0,0 +1,262 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +#ifndef __ESP_INTR_ALLOC_H__ +#define __ESP_INTR_ALLOC_H__ + +#include +#include +#include "esp_err.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +/** @addtogroup Intr_Alloc + * @{ + */ + + +/** @brief Interrupt allocation flags + * + * These flags can be used to specify which interrupt qualities the + * code calling esp_intr_alloc* needs. + * + */ + +//Keep the LEVELx values as they are here; they match up with (1<3 + * is requested, because these types of interrupts aren't C-callable. + * @param arg Optional argument for passed to the interrupt handler + * @param ret_handle Pointer to an int_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, int_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. + * @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 int_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, int_handle_t *ret_handle); + + +/** + * @brief Disable and free an interrupt. + * + * Use an interrupt handle to disable the interrupt (if non-shared) and release the resources + * associated with it. + * + * @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(int_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(int_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(int_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. + * + * @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); + +/** + * @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. + * + * @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); + + +/** + * @brief Disable interrupts that aren't specifically marked as running from IRAM + */ +void esp_intr_noniram_disable(); + + +/** + * @brief Re-enable interrupts disabled by esp_intr_noniram_disable + */ +void esp_intr_noniram_enable(); + +/**@}*/ + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/components/esp32/intr_alloc.c b/components/esp32/intr_alloc.c new file mode 100644 index 0000000000..bd7a7e2c25 --- /dev/null +++ b/components/esp32/intr_alloc.c @@ -0,0 +1,654 @@ +// Copyright 2015-2016 Espressif Systems (Shanghai) PTE LTD +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at + +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + + + +#include "sdkconfig.h" +#include +#include +#include +#include +#include +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" +#include +#include "esp_err.h" +#include "esp_log.h" +#include "esp_intr.h" +#include "esp_attr.h" +#include "esp_intr_alloc.h" +#include +#include + +static const char* TAG = "intr_alloc"; + + +#define ETS_INTERNAL_TIMER0_INTR_NO 6 +#define ETS_INTERNAL_TIMER1_INTR_NO 15 +#define ETS_INTERNAL_TIMER2_INTR_NO 16 +#define ETS_INTERNAL_SW0_INTR_NO 7 +#define ETS_INTERNAL_SW1_INTR_NO 29 +#define ETS_INTERNAL_PROFILING_INTR_NO 11 + + +/* +Define this to debug the choices made when allocating the interrupt. This leads to much debugging +output within a critical region, which can lead to weird effects like e.g. the interrupt watchdog +being triggered, that is why it is separate from the normal LOG* scheme. +*/ +//define DEBUG_INT_ALLOC_DECISIONS +#ifdef DEBUG_INT_ALLOC_DECISIONS +# define ALCHLOG(...) ESP_EARLY_LOGD(TAG, __VA_ARGS__) +#else +# define ALCHLOG(...) do {} while (0) +#endif + + +typedef enum { + INTDESC_NORMAL=0, + INTDESC_RESVD, + INTDESC_SPECIAL //for xtensa timers / software ints +} int_desc_flag_t; + +typedef enum { + INTTP_LEVEL=0, + INTTP_EDGE, + INTTP_NA +} int_type_t; + +typedef struct { + int level; + int_type_t type; + int_desc_flag_t cpuflags[2]; +} int_desc_t; + + +//We should mark the interrupt for the timer used by FreeRTOS as reserved. The specific timer +//is selectable using menuconfig; we use these cpp bits to convert that into something we can use in +//the table below. +#if CONFIG_FREERTOS_CORETIMER_0 +#define INT6RES INTDESC_RESVD +#else +#define INT6RES INTDESC_SPECIAL +#endif + +#if CONFIG_FREERTOS_CORETIMER_1 +#define INT15RES INTDESC_RESVD +#else +#define INT15RES INTDESC_SPECIAL +#endif + +#if CONFIG_FREERTOS_CORETIMER_2 +#define INT16RES INTDESC_RESVD +#else +#define INT16RES 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_RESVD, INTDESC_RESVD } }, //2 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //3 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_NORMAL} }, //4 + { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_NORMAL} }, //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_RESVD, 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, {INT16RES, INT16RES } }, //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 +}; + + +//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 { + 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) + +struct vector_desc_t { + int intno_cpu; //intno+cpu*32 + int flags; //OR of VECDESC_FLAG_* defines + shared_vector_desc_t *shared_vec_info; //used when VECDESC_FL_SHARED + vector_desc_t *next; +}; + +struct int_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 +static vector_desc_t *vector_desc_head; + +//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]; + + +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. +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; + prev=vd; + vd=vd->next; + } + if (vd==NULL && prev==NULL) { + //First item + vector_desc_head=to_insert; + vector_desc_head->next=NULL; + } 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->intno_cpu==to_intno_cpu(intno, cpu)) 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. +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=malloc(sizeof(vector_desc_t)); + memset(newvd, 0, sizeof(vector_desc_t)); + newvd->intno_cpu=to_intno_cpu(intno, cpu); + insert_vector_desc(newvd); + return newvd; + } else { + 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); + 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); + 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); +} + + +//Locate a free interrupt compatible with the flags given. +//The 'force' argument can be -1, or 0-31 to force checking a certain interrupt. +static int get_free_int(int flags, int cpu, int force) +{ + 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(TAG, "get_free_int: start looking. Current cpu: %d", cpu); + //Iterate over the 32 possible interrupts + for (x=0; x!=31; x++) { + //Grab the vector_desc for this vector. + vector_desc_t *vd=find_desc_for_int(x, cpu); + if (vd==NULL) vd=&empty_vect_desc; + //See if we have a forced interrupt; if so, bail out if this is not it. + if (force!=-1 && force!=x) { + ALCHLOG(TAG, "Ignoring int %d: forced to %d", x, force); + continue; + } + ALCHLOG(TAG, "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)); + //Check if interrupt is not reserved by design + if (int_desc[x].cpuflags[cpu]==INTDESC_RESVD) { //ToDo: Check for SPECIAL and force!=-1 + ALCHLOG(TAG, "....Unusable: reserved"); + continue; + } + //Check if the interrupt level is acceptable + if (!(flags&(1<flags&VECDESC_FL_SHARED)) { + ALCHLOG(TAG, "....Unusable: already allocated"); + continue; + } + //Ints can't be both shared and non-shared. + assert(!((vd->flags&VECDESC_FL_SHARED)&&(vd->flags&VECDESC_FL_NONSHARED))); + //check if interrupt is reserved at runtime + if (vd->flags&VECDESC_FL_RESERVED) { + ALCHLOG(TAG, "....Unusable: reserved at runtime."); + continue; + } + //check if interrupt already is in use by a non-shared interrupt + if (vd->flags&VECDESC_FL_NONSHARED) { + ALCHLOG(TAG, "....Unusable: already in (non-shared) use."); + continue; + } + if (flags&ESP_INTR_FLAG_SHARED) { + //We're allocating a shared int. + 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(TAG, "....Unusable: shared but iram prop doesn't match"); + continue; + } + //See if int already is used as a shared interrupt. + if (vd->flags&VECDESC_FL_SHARED) { + //We can use this already-marked-as-shared interrupt. Count the already attached isrs in order to see + //how useful it is. + int no=0; + shared_vector_desc_t *svdesc=vd->shared_vec_info; + while (svdesc!=NULL) { + no++; + svdesc=svdesc->next; + } + if (noint_desc[x].level) { + //Seems like this shared vector is both okay and has the least amount of ISRs already attached to it. + best=x; + bestSharedCt=no; + bestLevel=int_desc[x].level; + ALCHLOG(TAG, "...int %d more usable as a shared int: has %d existing vectors", x, no); + } else { + ALCHLOG(TAG, "...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(TAG, "...int %d usable as a new shared int", x); + } + } else { + ALCHLOG(TAG, "...already have a shared int"); + } + } + } else { + //We need an unshared IRQ; can't use shared ones; bail out if this is shared. + if (vd->flags&VECDESC_FL_SHARED) { + ALCHLOG(TAG, "...Unusable: int is shared, we need non-shared."); + continue; + } + //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(TAG, "...worse than int %d", best); + } + } + } + ALCHLOG(TAG, "get_free_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->statusreg == NULL) || (*sh_vec->statusreg & sh_vec->statusmask)) { + sh_vec->isr(sh_vec->arg); + sh_vec=sh_vec->next; + } + } + portEXIT_CRITICAL(&spinlock); +} + + +//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) +{ + 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 + 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; + //Statusreg should have a mask + if (intrstatusreg && !intrstatusmask) 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; + + portENTER_CRITICAL(&spinlock); + int cpu=xPortGetCoreID(); + //See if we can find an interrupt that matches the flags. + int intr=get_free_int(flags, cpu, force); + if (intr==-1) { + //None found. Bail out. + portEXIT_CRITICAL(&spinlock); + return ESP_ERR_NOT_FOUND; + } + //Get an int vector desc for int. + vector_desc_t *vd=get_desc_for_int(intr, cpu); + + //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)); + 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; + 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) { + xt_set_interrupt_handler(intr, handler, arg); + } + if (flags&ESP_INTR_FLAG_EDGE) xthal_set_intclear(1 << intr); + } + if (flags&ESP_INTR_FLAG_IRAM) { + vd->flags|=VECDESC_FL_INIRAM; + non_iram_int_mask[cpu]&=~(1<flags&=~VECDESC_FL_INIRAM; + non_iram_int_mask[cpu]|=(1<=0) { + intr_matrix_set(cpu, source, intr); + } + //If we should return a handle, allocate it here. + if (ret_handle!=NULL) { + int_handle_data_t *ret; + ret=malloc(sizeof(int_handle_data_t)); + ret->vector_desc=vd; + ret->shared_vector_desc=vd->shared_vec_info; + *ret_handle=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) +{ + /* + 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 esp_intr_free(int_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; + + portENTER_CRITICAL(&spinlock); + 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"); + //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); + //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))); + } + portEXIT_CRITICAL(&spinlock); + free(handle); + return ESP_OK; +} + +int esp_intr_get_intno(int_handle_t handle) +{ + return to_intno(handle->vector_desc->intno_cpu); +} + +int esp_intr_get_cpu(int_handle_t handle) +{ + return to_cpu(handle->vector_desc->intno_cpu); +} + +esp_err_t esp_intr_enable(int_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)); + return ESP_OK; +} + +esp_err_t esp_intr_disable(int_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)); + return ESP_OK; +} + + +void esp_intr_noniram_disable() +{ + int oldint; + int cpu=xPortGetCoreID(); + int intmask=~non_iram_int_mask[cpu]; + assert(non_iram_int_disabled_flag[cpu]==false); + 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 esp_intr_noniram_enable() +{ + int cpu=xPortGetCoreID(); + int intmask=non_iram_int_disabled[cpu]; + assert(non_iram_int_disabled_flag[cpu]==true); + 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"); +} + + + + + + diff --git a/components/esp32/task_wdt.c b/components/esp32/task_wdt.c index 860556b8c5..8585260e2f 100644 --- a/components/esp32/task_wdt.c +++ b/components/esp32/task_wdt.c @@ -27,6 +27,7 @@ #include #include "esp_err.h" #include "esp_intr.h" +#include "esp_intr_alloc.h" #include "esp_attr.h" #include "esp_freertos_hooks.h" #include "soc/timer_group_struct.h" @@ -51,7 +52,7 @@ static wdt_task_t *wdt_task_list=NULL; static portMUX_TYPE taskwdt_spinlock = portMUX_INITIALIZER_UNLOCKED; -static void IRAM_ATTR task_wdt_isr(void *arg) { +static void task_wdt_isr(void *arg) { wdt_task_t *wdttask; const char *cpu; //Feed the watchdog so we do not reset @@ -71,21 +72,21 @@ static void IRAM_ATTR task_wdt_isr(void *arg) { return; } //Watchdog got triggered because at least one task did not report in. - ets_printf(DRAM_STR("Task watchdog got triggered. The following tasks did not feed the watchdog in time:\n")); + ets_printf("Task watchdog got triggered. The following tasks did not feed the watchdog in time:\n"); for (wdttask=wdt_task_list; wdttask!=NULL; wdttask=wdttask->next) { if (!wdttask->fed_watchdog) { cpu=xTaskGetAffinity(wdttask->task_handle)==0?DRAM_STR("CPU 0"):DRAM_STR("CPU 1"); if (xTaskGetAffinity(wdttask->task_handle)==tskNO_AFFINITY) cpu=DRAM_STR("CPU 0/1"); - ets_printf(DRAM_STR(" - %s (%s)\n"), pcTaskGetTaskName(wdttask->task_handle), cpu); + ets_printf(" - %s (%s)\n", pcTaskGetTaskName(wdttask->task_handle), cpu); } } ets_printf(DRAM_STR("Tasks currently running:\n")); for (int x=0; x #include #include "esp_attr.h" +#include "esp_intr_alloc.h" #include "soc/soc.h" #include "soc/rtc_cntl_reg.h" #include "soc/frc_timer_reg.h" @@ -105,9 +106,7 @@ void esp_setup_time_syscalls() SET_PERI_REG_MASK(FRC_TIMER_CTRL_REG(0), FRC_TIMER_ENABLE | \ FRC_TIMER_INT_ENABLE); - intr_matrix_set(xPortGetCoreID(), ETS_TIMER1_INTR_SOURCE, ETS_FRC1_INUM); - xt_set_interrupt_handler(ETS_FRC1_INUM, &frc_timer_isr, NULL); - xt_ints_on(1 << ETS_FRC1_INUM); + esp_intr_alloc(ETS_TIMER1_INTR_SOURCE, 0, &frc_timer_isr, NULL, NULL); #endif // WITH_FRC1 } diff --git a/components/spi_flash/cache_utils.c b/components/spi_flash/cache_utils.c index 904007b316..f30db80cd8 100644 --- a/components/spi_flash/cache_utils.c +++ b/components/spi_flash/cache_utils.c @@ -27,6 +27,7 @@ #include "sdkconfig.h" #include "esp_ipc.h" #include "esp_attr.h" +#include "esp_intr_alloc.h" #include "esp_spi_flash.h" #include "esp_log.h" @@ -60,6 +61,8 @@ void IRAM_ATTR spi_flash_op_block_func(void* arg) { // Disable scheduler on this CPU vTaskSuspendAll(); + // Restore interrupts that aren't located in IRAM + esp_intr_noniram_disable(); uint32_t cpuid = (uint32_t) arg; // Disable cache so that flash operation can start spi_flash_disable_cache(cpuid, &s_flash_op_cache_state[cpuid]); @@ -70,6 +73,8 @@ void IRAM_ATTR spi_flash_op_block_func(void* arg) } // Flash operation is complete, re-enable cache spi_flash_restore_cache(cpuid, s_flash_op_cache_state[cpuid]); + // Restore interrupts that aren't located in IRAM + esp_intr_noniram_enable(); // Re-enable scheduler xTaskResumeAll(); } @@ -104,6 +109,8 @@ void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu() // occupied by highest priority task assert(xPortGetCoreID() == cpuid); } + // Kill interrupts that aren't located in IRAM + esp_intr_noniram_disable(); // Disable cache on this CPU as well spi_flash_disable_cache(cpuid, &s_flash_op_cache_state[cpuid]); } @@ -130,6 +137,8 @@ void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu() } // Release API lock spi_flash_op_unlock(); + // Re-enable non-iram interrupts + esp_intr_noniram_enable(); } #else // CONFIG_FREERTOS_UNICORE @@ -151,6 +160,7 @@ void spi_flash_op_unlock() void IRAM_ATTR spi_flash_disable_interrupts_caches_and_other_cpu() { + esp_intr_noniram_disable(); spi_flash_op_lock(); spi_flash_disable_cache(0, &s_flash_op_cache_state[0]); } @@ -159,6 +169,7 @@ void IRAM_ATTR spi_flash_enable_interrupts_caches_and_other_cpu() { spi_flash_restore_cache(0, s_flash_op_cache_state[0]); spi_flash_op_unlock(); + esp_intr_noniram_enable(); } #endif // CONFIG_FREERTOS_UNICORE diff --git a/docs/Doxyfile b/docs/Doxyfile index c1aebcd308..bdb91a4dce 100755 --- a/docs/Doxyfile +++ b/docs/Doxyfile @@ -27,7 +27,8 @@ INPUT = ../components/esp32/include/esp_wifi.h \ ../components/esp32/include/esp_task_wdt.h \ ../components/app_update/include/esp_ota_ops.h \ ../components/ethernet/include/esp_eth.h \ - ../components/ulp/include/esp32/ulp.h + ../components/ulp/include/esp32/ulp.h \ + ../components/esp32/include/esp_intr_alloc.h ## Get warnings for functions that have no documentation for their parameters or return value ## diff --git a/docs/api/intr_alloc.rst b/docs/api/intr_alloc.rst new file mode 100644 index 0000000000..e4e9bdb4ad --- /dev/null +++ b/docs/api/intr_alloc.rst @@ -0,0 +1,84 @@ +Interrupt allocation +==================== + +Overview +-------- + +The ESP32 has two cores, with 32 interrupts each. Each interrupt has a certain priority level, most (but not all) interrupts are connected +to the interrupt mux. Because there are more interrupt sources than interrupts, sometimes it makes sense to share an interrupt in +multiple drivers. The esp_intr_alloc abstraction exists to hide all these implementation details. + +A driver can allocate an interrupt for a certain peripheral by calling esp_intr_alloc (or esp_intr_alloc_sintrstatus). It can use +the flags passed to this function to set the type of interrupt allocated, specifying a specific level or trigger method. The +interrupt allocation code will then find an applicable interrupt, use the interrupt mux to hook it up to the peripheral, and +install the given interrupt handler and ISR to it. + +This code has two different types of interrupts it handles differently: Shared interrupts and non-shared interrupts. The simplest +of the two are non-shared interrupts: a separate interrupt is allocated per esp_intr_alloc call and this interrupt is solely used for +the peripheral attached to it, with only one ISR that will get called. Non-shared interrupts can have multiple peripherals triggering +it, with multiple ISRs being called when one of the peripherals attached signals an interrupt. Thus, ISRs that are intended for shared +interrupts should check the interrupt status of the peripheral they service in order to see if any action is required. + +Non-shared interrupts can be either level- or edge-triggered. Shared interrupts can +only be level interrupts (because of the chance of missed interrupts when edge interrupts are +used.) +(The logic behind this: DevA and DevB share an int. DevB signals an int. Int line goes high. ISR handler +calls code for DevA -> does nothing. ISR handler calls code for DevB, but while doing that, +DevA signals an int. ISR DevB is done, clears int for DevB, exits interrupt code. Now an +interrupt for DevA is still pending, but because the int line never went low (DevA kept it high +even when the int for DevB was cleared) the interrupt is never serviced.) + + + + +Application Example +------------------- + +API Reference +------------- + +Header Files +^^^^^^^^^^^^ + + * `esp_intr_alloc.h `_ + + +Macros +^^^^^^ + +.. doxygendefine:: ESP_INTR_FLAG_LEVEL1 +.. doxygendefine:: ESP_INTR_FLAG_LEVEL2 +.. doxygendefine:: ESP_INTR_FLAG_LEVEL3 +.. doxygendefine:: ESP_INTR_FLAG_LEVEL4 +.. doxygendefine:: ESP_INTR_FLAG_LEVEL5 +.. doxygendefine:: ESP_INTR_FLAG_LEVEL6 +.. doxygendefine:: ESP_INTR_FLAG_NMI +.. doxygendefine:: ESP_INTR_FLAG_LOWMED +.. doxygendefine:: ESP_INTR_FLAG_HIGH +.. doxygendefine:: ESP_INTR_FLAG_SHARED +.. doxygendefine:: ESP_INTR_FLAG_EDGE +.. doxygendefine:: ESP_INTR_FLAG_IRAM + +Type Definitions +^^^^^^^^^^^^^^^^ + +Enumerations +^^^^^^^^^^^^ + +Structures +^^^^^^^^^^ + +Functions +^^^^^^^^^ + +.. doxygenfunction:: esp_intr_mark_shared +.. doxygenfunction:: esp_intr_reserve +.. doxygenfunction:: esp_intr_alloc +.. doxygenfunction:: esp_intr_alloc_intrstatus +.. doxygenfunction:: esp_intr_free +.. doxygenfunction:: esp_intr_get_cpu +.. doxygenfunction:: esp_intr_get_intno +.. doxygenfunction:: esp_intr_disable +.. doxygenfunction:: esp_intr_enable +.. doxygenfunction:: esp_intr_noniram_disable +.. doxygenfunction:: esp_intr_noniram_enable diff --git a/docs/index.rst b/docs/index.rst index 4c48255ae2..4e58cfe02f 100755 --- a/docs/index.rst +++ b/docs/index.rst @@ -110,6 +110,7 @@ Contents: Non-Volatile Storage Virtual Filesystem Ethernet + Interrupt Allocation deep-sleep-stub Template diff --git a/examples/11_rmt_nec_tx_rx/main/infrared_nec.c b/examples/11_rmt_nec_tx_rx/main/infrared_nec.c index dbd11fbae5..d2f7b091fa 100644 --- a/examples/11_rmt_nec_tx_rx/main/infrared_nec.c +++ b/examples/11_rmt_nec_tx_rx/main/infrared_nec.c @@ -43,7 +43,6 @@ static const char* NEC_TAG = "NEC"; #define RMT_TX_GPIO_NUM 16 /*!< GPIO number for transmitter signal */ #define RMT_RX_CHANNEL 0 /*!< RMT channel for receiver */ #define RMT_RX_GPIO_NUM 19 /*!< GPIO number for receiver */ -#define RMT_INTR_NUM 19 /*!< RMT interrupt number, select from soc.h */ #define RMT_CLK_DIV 100 /*!< RMT counter clock divider */ #define RMT_TICK_10_US (80000000/RMT_CLK_DIV/100000) /*!< RMT counter value for 10 us.(Source clock is APB clock) */ @@ -254,7 +253,7 @@ static void rmt_tx_init() rmt_tx.tx_config.idle_output_en = true; rmt_tx.rmt_mode = 0; rmt_config(&rmt_tx); - rmt_driver_install(rmt_tx.channel, 0, RMT_INTR_NUM); + rmt_driver_install(rmt_tx.channel, 0, 0); } /* @@ -272,7 +271,7 @@ void rmt_rx_init() rmt_rx.rx_config.filter_ticks_thresh = 100; rmt_rx.rx_config.idle_threshold = rmt_item32_tIMEOUT_US / 10 * (RMT_TICK_10_US); rmt_config(&rmt_rx); - rmt_driver_install(rmt_rx.channel, 1000, RMT_INTR_NUM); + rmt_driver_install(rmt_rx.channel, 1000, 0); } /** diff --git a/examples/13_timer_group/main/timer_group.c b/examples/13_timer_group/main/timer_group.c index 15d1ca2c05..e91efe767d 100644 --- a/examples/13_timer_group/main/timer_group.c +++ b/examples/13_timer_group/main/timer_group.c @@ -16,8 +16,6 @@ #include "driver/periph_ctrl.h" #include "driver/timer.h" -#define TIMER_INTR_NUM_0 17 /*!< Interrupt number for hardware timer 0 */ -#define TIMER_INTR_NUM_1 18 /*!< Interrupt number for hardware timer 1*/ #define TIMER_INTR_SEL TIMER_INTR_LEVEL /*!< Timer level interrupt */ #define TIMER_GROUP TIMER_GROUP_0 /*!< Test on timer group 0 */ #define TIMER_DIVIDER 16 /*!< Hardware timer clock divider */ @@ -157,7 +155,7 @@ void tg0_timer0_init() /*Enable timer interrupt*/ timer_enable_intr(timer_group, timer_idx); /*Set ISR handler*/ - timer_isr_register(timer_group, timer_idx, TIMER_INTR_NUM_0, TIMER_INTR_SEL, timer_group0_isr, (void*) timer_idx); + timer_isr_register(timer_group, timer_idx, timer_group0_isr, (void*) timer_idx, 0); /*Start timer counter*/ timer_start(timer_group, timer_idx); } @@ -187,7 +185,7 @@ void tg0_timer1_init() /*Enable timer interrupt*/ timer_enable_intr(timer_group, timer_idx); /*Set ISR handler*/ - timer_isr_register(timer_group, timer_idx, TIMER_INTR_NUM_1, TIMER_INTR_SEL, timer_group0_isr, (void*) timer_idx); + timer_isr_register(timer_group, timer_idx, timer_group0_isr, (void*) timer_idx, 0); /*Start timer counter*/ timer_start(timer_group, timer_idx); } diff --git a/examples/16_pcnt/main/pcnt_test.c b/examples/16_pcnt/main/pcnt_test.c index 1da4cca56e..31d8f28870 100644 --- a/examples/16_pcnt/main/pcnt_test.c +++ b/examples/16_pcnt/main/pcnt_test.c @@ -42,7 +42,6 @@ static const char* TAG = "PCNT_TEST"; #define PCNT_L_LIM_VAL (-10) #define PCNT_THRESH1_VAL (5) #define PCNT_THRESH0_VAL (-5) -#define PCNT_INTR_NUM (18) #define PCNT_INPUT_SIG_IO (4) #define PCNT_INPUT_CTRL_IO (5) #define LEDC_OUPUT_IO (18) @@ -177,7 +176,7 @@ static void pcnt_init(void) /*Reset counter value*/ pcnt_counter_clear(PCNT_TEST_UNIT); /*Register ISR handler*/ - pcnt_isr_register(PCNT_INTR_NUM, pcnt_intr_handler, NULL); + pcnt_isr_register(pcnt_intr_handler, NULL, 0); /*Enable interrupt for PCNT unit*/ pcnt_intr_enable(PCNT_TEST_UNIT); /*Resume counting*/ From 32fa94935de99465a17948640ea0432b8b6721c4 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Tue, 6 Dec 2016 14:20:12 +0800 Subject: [PATCH 2/7] Changes according to merge request --- components/driver/include/driver/gpio.h | 4 --- components/driver/include/driver/ledc.h | 2 -- components/driver/include/driver/pcnt.h | 2 -- components/driver/include/driver/rmt.h | 1 - .../esp32/include/esp_heap_alloc_caps.h | 24 ++++++------- components/esp32/intr_alloc.c | 35 ++++++++++++++++--- components/mbedtls/Kconfig | 8 ----- 7 files changed, 42 insertions(+), 34 deletions(-) diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index 59dd568578..31d54d0993 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -346,10 +346,6 @@ esp_err_t gpio_wakeup_disable(gpio_num_t gpio_num); * @param fn Interrupt 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 - * Note that the handler function MUST be defined with attribution of "IRAM_ATTR". - * * @param arg Parameter for handler function * * @return diff --git a/components/driver/include/driver/ledc.h b/components/driver/include/driver/ledc.h index a581f604ec..3042aa2518 100644 --- a/components/driver/include/driver/ledc.h +++ b/components/driver/include/driver/ledc.h @@ -262,8 +262,6 @@ esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty, * @param arg User-supplied argument passed to the 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 - * Note that the handler function MUST be defined with attribution of "IRAM_ATTR". * @param arg Parameter for handler function * * @return diff --git a/components/driver/include/driver/pcnt.h b/components/driver/include/driver/pcnt.h index 1ab2abae74..cab324936c 100644 --- a/components/driver/include/driver/pcnt.h +++ b/components/driver/include/driver/pcnt.h @@ -215,8 +215,6 @@ esp_err_t pcnt_get_event_value(pcnt_unit_t unit, pcnt_evt_type_t evt_type, int16 * The handler will be attached to the same CPU core that this function is running on. * * @param fn Interrupt handler function. - * @note - * Note that the handler function MUST be defined with attribution of "IRAM_ATTR". * @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. diff --git a/components/driver/include/driver/rmt.h b/components/driver/include/driver/rmt.h index 4c28406d80..9e6882efbd 100644 --- a/components/driver/include/driver/rmt.h +++ b/components/driver/include/driver/rmt.h @@ -755,7 +755,6 @@ esp_err_t rmt_get_ringbuf_handler(rmt_channel_t channel, RingbufHandle_t* buf_ha * ----------------EXAMPLE OF INTERRUPT HANDLER --------------- * @code{c} * #include "esp_attr.h" - * //we should add 'IRAM_ATTR' attribution when we declare the isr function * void IRAM_ATTR rmt_isr_handler(void* arg) * { * //read RMT interrupt status. diff --git a/components/esp32/include/esp_heap_alloc_caps.h b/components/esp32/include/esp_heap_alloc_caps.h index d371ca5ed8..cb880d6a42 100644 --- a/components/esp32/include/esp_heap_alloc_caps.h +++ b/components/esp32/include/esp_heap_alloc_caps.h @@ -14,18 +14,18 @@ #ifndef HEAP_ALLOC_CAPS_H #define HEAP_ALLOC_CAPS_H -#define MALLOC_CAP_EXEC (1<<0) //Memory must be able to run executable code -#define MALLOC_CAP_32BIT (1<<1) //Memory must allow for aligned 32-bit data accesses -#define MALLOC_CAP_8BIT (1<<2) //Memory must allow for 8/16/...-bit data accesses -#define MALLOC_CAP_DMA (1<<3) //Memory must be able to accessed by DMA -#define MALLOC_CAP_PID2 (1<<4) //Memory must be mapped to PID2 memory space -#define MALLOC_CAP_PID3 (1<<5) //Memory must be mapped to PID3 memory space -#define MALLOC_CAP_PID4 (1<<6) //Memory must be mapped to PID4 memory space -#define MALLOC_CAP_PID5 (1<<7) //Memory must be mapped to PID5 memory space -#define MALLOC_CAP_PID6 (1<<8) //Memory must be mapped to PID6 memory space -#define MALLOC_CAP_PID7 (1<<9) //Memory must be mapped to PID7 memory space -#define MALLOC_CAP_SPISRAM (1<<10) //Memory must be in SPI SRAM -#define MALLOC_CAP_INVALID (1<<31) //Memory can't be used / list end marker +#define MALLOC_CAP_EXEC (1<<0) //Memory must be able to run executable code +#define MALLOC_CAP_32BIT (1<<1) //Memory must allow for aligned 32-bit data accesses +#define MALLOC_CAP_8BIT (1<<2) //Memory must allow for 8/16/...-bit data accesses +#define MALLOC_CAP_DMA (1<<3) //Memory must be able to accessed by DMA +#define MALLOC_CAP_PID2 (1<<4) //Memory must be mapped to PID2 memory space +#define MALLOC_CAP_PID3 (1<<5) //Memory must be mapped to PID3 memory space +#define MALLOC_CAP_PID4 (1<<6) //Memory must be mapped to PID4 memory space +#define MALLOC_CAP_PID5 (1<<7) //Memory must be mapped to PID5 memory space +#define MALLOC_CAP_PID6 (1<<8) //Memory must be mapped to PID6 memory space +#define MALLOC_CAP_PID7 (1<<9) //Memory must be mapped to PID7 memory space +#define MALLOC_CAP_SPISRAM (1<<10) //Memory must be in SPI SRAM +#define MALLOC_CAP_INVALID (1<<31) //Memory can't be used / list end marker void heap_alloc_caps_init(); diff --git a/components/esp32/intr_alloc.c b/components/esp32/intr_alloc.c index bd7a7e2c25..a13424af8c 100644 --- a/components/esp32/intr_alloc.c +++ b/components/esp32/intr_alloc.c @@ -225,12 +225,13 @@ static vector_desc_t *find_desc_for_int(int intno, int cpu) //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. +//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=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); insert_vector_desc(newvd); @@ -247,6 +248,10 @@ esp_err_t esp_intr_mark_shared(int intno, int cpu, bool is_int_ram) 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); @@ -261,6 +266,10 @@ esp_err_t esp_intr_reserve(int intno, int cpu) 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); @@ -301,7 +310,7 @@ static int get_free_int(int flags, int cpu, int force) ALCHLOG(TAG, "get_free_int: start looking. Current cpu: %d", cpu); //Iterate over the 32 possible interrupts - for (x=0; x!=31; x++) { + for (x=0; x<32; x++) { //Grab the vector_desc for this vector. vector_desc_t *vd=find_desc_for_int(x, cpu); if (vd==NULL) vd=&empty_vect_desc; @@ -430,6 +439,7 @@ static void IRAM_ATTR shared_intr_isr(void *arg) 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) { + int_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. @@ -462,6 +472,12 @@ 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; + } + portENTER_CRITICAL(&spinlock); int cpu=xPortGetCoreID(); //See if we can find an interrupt that matches the flags. @@ -469,15 +485,26 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre 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; @@ -506,10 +533,8 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre if (source>=0) { intr_matrix_set(cpu, source, intr); } - //If we should return a handle, allocate it here. + //Fill return handle if needed if (ret_handle!=NULL) { - int_handle_data_t *ret; - ret=malloc(sizeof(int_handle_data_t)); ret->vector_desc=vd; ret->shared_vector_desc=vd->shared_vec_info; *ret_handle=ret; diff --git a/components/mbedtls/Kconfig b/components/mbedtls/Kconfig index 1e347582f0..7c75d85c1f 100644 --- a/components/mbedtls/Kconfig +++ b/components/mbedtls/Kconfig @@ -61,14 +61,6 @@ config MBEDTLS_MPI_USE_INTERRUPT This allows other code to run on the CPU while an MPI operation is pending. Otherwise the CPU busy-waits. -config MBEDTLS_MPI_INTERRUPT_NUM - int "MPI Interrupt number" - depends on MBEDTLS_MPI_USE_INTERRUPT - default 18 - help - CPU interrupt number for MPI interrupt to connect to. Must be otherwise unused. - Eventually this assignment will be handled automatically at runtime. - config MBEDTLS_HARDWARE_SHA bool "Enable hardware SHA acceleration" default y From ae8c37e0b6c64725e045298cf890ce6d9fa0aaa0 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Wed, 7 Dec 2016 21:30:21 +0800 Subject: [PATCH 3/7] 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 | 186 ++++++++++++++-------- components/esp32/test/test_intr_alloc.c | 150 +++++++++++++++++ tools/unit-test-app/sdkconfig | 3 +- 16 files changed, 348 insertions(+), 102 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 - 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; } 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 # From 9dc908d105093f7dab1b7459a5bab40087877704 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Thu, 8 Dec 2016 12:04:26 +0800 Subject: [PATCH 4/7] Add test for local interrupts, fix int disable code --- components/esp32/include/esp_intr_alloc.h | 20 ++++---- components/esp32/intr_alloc.c | 3 +- components/esp32/test/test_intr_alloc.c | 56 +++++++++++++++++++++++ components/freertos/xtensa_intr_asm.S | 12 ++--- 4 files changed, 73 insertions(+), 18 deletions(-) diff --git a/components/esp32/include/esp_intr_alloc.h b/components/esp32/include/esp_intr_alloc.h index 02c841c3d9..c1f91dd2e3 100644 --- a/components/esp32/include/esp_intr_alloc.h +++ b/components/esp32/include/esp_intr_alloc.h @@ -93,7 +93,7 @@ typedef intr_handle_data_t* intr_handle_t ; * @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 out. + * 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 @@ -131,7 +131,8 @@ esp_err_t esp_intr_reserve(int intno, int cpu); * 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. + * 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 @@ -164,7 +165,8 @@ esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *ar * 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. + * 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 @@ -186,7 +188,7 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre /** * @brief Disable and free an interrupt. * - * Use an interrupt handle to disable the interrupt (if non-shared) and release the resources + * Use an interrupt handle to disable the interrupt and release the resources * associated with it. * * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus @@ -220,9 +222,8 @@ 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. + * @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 * @@ -234,9 +235,8 @@ 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. + * @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 * diff --git a/components/esp32/intr_alloc.c b/components/esp32/intr_alloc.c index 9eec38c42f..f17c56bc20 100644 --- a/components/esp32/intr_alloc.c +++ b/components/esp32/intr_alloc.c @@ -437,7 +437,6 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre { 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; @@ -574,8 +573,8 @@ esp_err_t esp_intr_free(intr_handle_t handle) //This routine should be called from the interrupt the task is scheduled on. if (handle->vector_desc->cpu!=xPortGetCoreID()) return ESP_ERR_INVALID_ARG; - esp_intr_disable(handle); 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; diff --git a/components/esp32/test/test_intr_alloc.c b/components/esp32/test/test_intr_alloc.c index 30f7d4bab9..390079aa46 100644 --- a/components/esp32/test/test_intr_alloc.c +++ b/components/esp32/test/test_intr_alloc.c @@ -138,6 +138,62 @@ static void timer_test(int flags) { 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() +{ + 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_RATE_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_RATE_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_RATE_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_RATE_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_RATE_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", "[esp32]") +{ + local_timer_test(); +} TEST_CASE("Intr_alloc test, private ints", "[esp32]") { diff --git a/components/freertos/xtensa_intr_asm.S b/components/freertos/xtensa_intr_asm.S index aff4e3cbc2..330b68f592 100644 --- a/components/freertos/xtensa_intr_asm.S +++ b/components/freertos/xtensa_intr_asm.S @@ -204,12 +204,12 @@ xt_ints_off: wsr a5, INTENABLE /* Reenable interrupts */ mov a2, a3 /* Previous mask */ #else - movi a3, 0 - xsr a3, INTENABLE /* Disables all interrupts */ - or a2, a3, a2 /* set bits in mask */ - xor a2, a3, a2 /* invert bits in mask set in mask, essentially clearing them */ - wsr a2, INTENABLE /* Re-enable ints */ - mov a2, a3 /* return prev mask */ + movi a4, 0 + xsr a4, INTENABLE /* Disables all interrupts */ + or a3, a4, a2 /* set bits in mask */ + xor a3, a3, a2 /* invert bits in mask set in mask, essentially clearing them */ + wsr a3, INTENABLE /* Re-enable ints */ + mov a2, a4 /* return prev mask */ #endif #else movi a2, 0 /* return zero */ From 2c34ab3374c9ad58152f17cc18d8e2d7f24f84f0 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Thu, 8 Dec 2016 12:38:22 +0800 Subject: [PATCH 5/7] Mark some interrupts that are now allocated dynamically as free, add int handle param documentation, add local ints test --- components/driver/include/driver/gpio.h | 2 + components/driver/include/driver/ledc.h | 2 + components/driver/include/driver/pcnt.h | 2 + components/driver/include/driver/timer.h | 7 +- components/driver/include/driver/uart.h | 12 ++ components/driver/uart.c | 13 ++ components/esp32/include/soc/soc.h | 9 +- components/esp32/intr_alloc.c | 6 +- components/esp32/test/test_intr_alloc.c | 217 ++++++++++----------- examples/13_timer_group/main/timer_group.c | 8 +- 10 files changed, 154 insertions(+), 124 deletions(-) diff --git a/components/driver/include/driver/gpio.h b/components/driver/include/driver/gpio.h index 7f178677bc..fba013fe8b 100644 --- a/components/driver/include/driver/gpio.h +++ b/components/driver/include/driver/gpio.h @@ -351,6 +351,8 @@ esp_err_t gpio_wakeup_disable(gpio_num_t gpio_num); * @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 arg Parameter for handler function + * @param handle Pointer to return handle. If non-NULL, a handle for the interrupt will + * be returned here. * * @return * - ESP_OK Success ; diff --git a/components/driver/include/driver/ledc.h b/components/driver/include/driver/ledc.h index 6430523252..fb97c6c011 100644 --- a/components/driver/include/driver/ledc.h +++ b/components/driver/include/driver/ledc.h @@ -265,6 +265,8 @@ esp_err_t ledc_set_fade(ledc_mode_t speed_mode, uint32_t channel, uint32_t duty, * @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 arg Parameter for handler function + * @param handle Pointer to return handle. If non-NULL, a handle for the interrupt will + * be returned here. * * @return * - ESP_OK Success diff --git a/components/driver/include/driver/pcnt.h b/components/driver/include/driver/pcnt.h index d852f8fe79..f5a10581c0 100644 --- a/components/driver/include/driver/pcnt.h +++ b/components/driver/include/driver/pcnt.h @@ -221,6 +221,8 @@ esp_err_t pcnt_get_event_value(pcnt_unit_t unit, pcnt_evt_type_t evt_type, int16 * @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 handle Pointer to return handle. If non-NULL, a handle for the interrupt will + * be returned here. * * @return * - ESP_OK Success diff --git a/components/driver/include/driver/timer.h b/components/driver/include/driver/timer.h index f46e708734..134fd504fb 100644 --- a/components/driver/include/driver/timer.h +++ b/components/driver/include/driver/timer.h @@ -258,12 +258,15 @@ esp_err_t timer_set_alarm(timer_group_t group_num, timer_idx_t timer_num, timer_ * @param timer_num Timer index of timer group * @param fn Interrupt handler function. * @note - * Code inside the handler function can only call functions in IRAM, so cannot call other timer APIs. - * Use direct register access to access timers from inside the ISR. + * In case the this is called with the INIRAM flag, code inside the handler function can + * only call functions in IRAM, so it cannot call other timer APIs. + * Use direct register access to access timers from inside the ISR in this case. * * @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 handle Pointer to return handle. If non-NULL, a handle for the interrupt will + * be returned here. * @return * - ESP_OK Success * - ESP_ERR_INVALID_ARG Function pointer error. diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index be3a6c8fc6..dd69dce275 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -379,6 +379,18 @@ esp_err_t uart_enable_tx_intr(uart_port_t uart_num, int enable, int thresh); */ esp_err_t uart_isr_register(uart_port_t uart_num, void (*fn)(void*), void * arg, int intr_alloc_flags); + +/** + * @brief Free UART interrupt handler registered by uart_isr_register. + * + * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + */ +esp_err_t uart_isr_free(uart_port_t uart_num); + /** * @brief Set UART pin number * diff --git a/components/driver/uart.c b/components/driver/uart.c index 7ce560f622..baf2d1f5a9 100644 --- a/components/driver/uart.c +++ b/components/driver/uart.c @@ -309,6 +309,19 @@ esp_err_t uart_isr_register(uart_port_t uart_num, void (*fn)(void*), void * arg, return ret; } + +esp_err_t uart_isr_free(uart_port_t uart_num) +{ + esp_err_t ret; + UART_CHECK((uart_num < UART_NUM_MAX), "uart_num error", ESP_FAIL); + if (p_uart_obj[uart_num]->intr_handle==NULL) return ESP_ERR_INVALID_ARG; + UART_ENTER_CRITICAL(&uart_spinlock[uart_num]); + ret=esp_intr_free(p_uart_obj[uart_num]->intr_handle); + p_uart_obj[uart_num]->intr_handle=NULL; + UART_EXIT_CRITICAL(&uart_spinlock[uart_num]); + return ret; +} + //internal signal can be output to multiple GPIO pads //only one GPIO pad can connect with input signal esp_err_t uart_set_pin(uart_port_t uart_num, int tx_io_num, int rx_io_num, int rts_io_num, int cts_io_num) diff --git a/components/esp32/include/soc/soc.h b/components/esp32/include/soc/soc.h index 3991152f21..511456a103 100755 --- a/components/esp32/include/soc/soc.h +++ b/components/esp32/include/soc/soc.h @@ -264,14 +264,14 @@ * Intr num Level Type PRO CPU usage APP CPU uasge * 0 1 extern level WMAC Reserved * 1 1 extern level BT/BLE Host VHCI Reserved - * 2 1 extern level FROM_CPU FROM_CPU - * 3 1 extern level TG0_WDT Reserved + * 2 1 extern level + * 3 1 extern level * 4 1 extern level WBB * 5 1 extern level BT Controller * 6 1 timer FreeRTOS Tick(L1) FreeRTOS Tick(L1) * 7 1 software Reserved Reserved * 8 1 extern level BLE Controller - * 9 1 extern level EMAC + * 9 1 extern level * 10 1 extern edge Internal Timer * 11 3 profiling * 12 1 extern level @@ -300,10 +300,7 @@ //CPU0 Interrupt number reserved, not touch this. #define ETS_WMAC_INUM 0 #define ETS_BT_HOST_INUM 1 -#define ETS_FROM_CPU_INUM 2 -#define ETS_T0_WDT_INUM 3 #define ETS_WBB_INUM 4 -#define ETS_EMAC_INUM 9 #define ETS_TG0_T1_INUM 10 /**< use edge interrupt*/ #define ETS_FRC1_INUM 22 #define ETS_T1_WDT_INUM 24 diff --git a/components/esp32/intr_alloc.c b/components/esp32/intr_alloc.c index f17c56bc20..77572b1a57 100644 --- a/components/esp32/intr_alloc.c +++ b/components/esp32/intr_alloc.c @@ -99,8 +99,8 @@ typedef struct { 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_RESVD, INTDESC_RESVD } }, //2 - { 1, INTTP_LEVEL, {INTDESC_RESVD, INTDESC_RESVD } }, //3 + { 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_NORMAL} }, //5 { 1, INTTP_NA, {INT6RES, INT6RES } }, //6 @@ -602,7 +602,7 @@ esp_err_t esp_intr_free(intr_handle_t handle) if ((handle->vector_desc->flags&VECDESC_FL_NONSHARED) || free_shared_vector) { ESP_LOGV(TAG, "esp_intr_free: Disabling int, killing handler"); //Reset to normal handler - xt_set_interrupt_handler(handle->vector_desc->intno, xt_unhandled_interrupt, (void*)handle->vector_desc->intno); + 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. diff --git a/components/esp32/test/test_intr_alloc.c b/components/esp32/test/test_intr_alloc.c index 390079aa46..31991b4e41 100644 --- a/components/esp32/test/test_intr_alloc.c +++ b/components/esp32/test/test_intr_alloc.c @@ -1,5 +1,5 @@ /* - Test for multicore FreeRTOS. This test spins up threads, fiddles with queues etc. + Tests for the interrupt allocator. */ #include @@ -25,9 +25,6 @@ #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; @@ -55,38 +52,38 @@ static volatile int count[4]={0,0,0,0}; static void timer_isr(void *arg) { int timer_idx = (int)arg; - count[timer_idx]++; + 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; - } + 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; - } + 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; - } + 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); + 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); + 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]); @@ -96,111 +93,111 @@ static void timer_test(int flags) { 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); + 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]); + 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]); } static volatile int int_timer_ctr; void int_timer_handler(void *arg) { - xthal_set_ccompare(1, xthal_get_ccount()+8000000); - int_timer_ctr++; + xthal_set_ccompare(1, xthal_get_ccount()+8000000); + int_timer_ctr++; } void local_timer_test() { - 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_RATE_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_RATE_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_RATE_MS); - printf("Timer val after 1 sec: %d\n", int_timer_ctr); - TEST_ASSERT(int_timer_ctr!=0); + 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_RATE_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_RATE_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_RATE_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_RATE_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_RATE_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"); + 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_RATE_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_RATE_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", "[esp32]") { - local_timer_test(); + local_timer_test(); } TEST_CASE("Intr_alloc test, private ints", "[esp32]") { - timer_test(0); + timer_test(0); } TEST_CASE("Intr_alloc test, shared ints", "[esp32]") { - timer_test(ESP_INTR_FLAG_SHARED); + timer_test(ESP_INTR_FLAG_SHARED); } diff --git a/examples/13_timer_group/main/timer_group.c b/examples/13_timer_group/main/timer_group.c index e91efe767d..f61f39b2c5 100644 --- a/examples/13_timer_group/main/timer_group.c +++ b/examples/13_timer_group/main/timer_group.c @@ -86,7 +86,9 @@ void IRAM_ATTR timer_group0_isr(void *para) /*Timer0 is an example that don't reload counter value*/ TIMERG0.hw_timer[timer_idx].update = 1; - /*We don't call a API here because they are not declared with IRAM_ATTR*/ + /* We don't call a API here because they are not declared with IRAM_ATTR. + If we're okay with the timer irq not being serviced while SPI flash cache is disabled, + we can alloc this interrupt without the ESP_INTR_FLAG_IRAM flag and use the normal API. */ TIMERG0.int_clr_timers.t0 = 1; uint64_t timer_val = ((uint64_t) TIMERG0.hw_timer[timer_idx].cnt_high) << 32 | TIMERG0.hw_timer[timer_idx].cnt_low; @@ -155,7 +157,7 @@ void tg0_timer0_init() /*Enable timer interrupt*/ timer_enable_intr(timer_group, timer_idx); /*Set ISR handler*/ - timer_isr_register(timer_group, timer_idx, timer_group0_isr, (void*) timer_idx, 0); + timer_isr_register(timer_group, timer_idx, timer_group0_isr, (void*) timer_idx, ESP_INTR_FLAG_IRAM); /*Start timer counter*/ timer_start(timer_group, timer_idx); } @@ -185,7 +187,7 @@ void tg0_timer1_init() /*Enable timer interrupt*/ timer_enable_intr(timer_group, timer_idx); /*Set ISR handler*/ - timer_isr_register(timer_group, timer_idx, timer_group0_isr, (void*) timer_idx, 0); + timer_isr_register(timer_group, timer_idx, timer_group0_isr, (void*) timer_idx, ESP_INTR_FLAG_IRAM); /*Start timer counter*/ timer_start(timer_group, timer_idx); } From cb9ef19d3bdf8ee73f5848387cc2334dc05c34d2 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Thu, 8 Dec 2016 12:57:57 +0800 Subject: [PATCH 6/7] Also add intr_alloc magic to rtc code --- components/driver/include/driver/touch_pad.h | 45 +++++++++----------- components/driver/rtc_module.c | 9 +--- components/esp32/lib | 2 +- tools/unit-test-app/sdkconfig | 5 +++ 4 files changed, 28 insertions(+), 33 deletions(-) diff --git a/components/driver/include/driver/touch_pad.h b/components/driver/include/driver/touch_pad.h index b8dc6e7534..4f78b2351b 100644 --- a/components/driver/include/driver/touch_pad.h +++ b/components/driver/include/driver/touch_pad.h @@ -19,6 +19,7 @@ extern "C" { #endif #include "esp_intr.h" #include "esp_err.h" +#include "esp_intr_alloc.h" #define TOUCH_PAD_SLEEP_CYCLE_CONFIG (0x1000)//The Time is 150Khz,the Max value is 0xffff #define TOUCH_PAD_MEASURE_CYCLE_CONFIG (0xffff)//The Time is 8Mhz,the Max value is 0xffff typedef enum { @@ -34,6 +35,9 @@ typedef enum { TOUCH_PAD_NUM9, /*!< Touch pad channel 0 is GPIO32*/ TOUCH_PAD_MAX, } touch_pad_t; + +typedef intr_handle_t touch_isr_handle_t; + /** * @brief Initialize touch module. * @@ -79,44 +83,40 @@ esp_err_t touch_pad_read(touch_pad_t touch_num, uint16_t * touch_value); /** * @brief register TouchPad interrupt handler, the handler is an ISR. * The handler will be attached to the same CPU core that this function is running on. - * @note - * Users should know that which CPU is running and then pick a INUM that is not used by system. - * We can find the information of INUM and interrupt level in soc.h. * - * @param touch_intr_num Touch interrupt number,check the info in soc.h, and please see the core-isa.h for more details * @param fn Interrupt handler function. - * - * @note - * Note that the handler function MUST be defined with attribution of "IRAM_ATTR". - * * @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 handle Pointer to return handle. If non-NULL, a handle for the interrupt will + * be returned here. * * @return * - ESP_OK Success ; * - ESP_ERR_INVALID_ARG GPIO error */ -esp_err_t touch_pad_isr_handler_register(uint32_t touch_intr_num, void(*fn)(void*), void *arg); +esp_err_t touch_pad_isr_handler_register(void(*fn)(void *), void *arg, int intr_alloc_flags, touch_isr_handle_t *handle); /** * *************** ATTENTION ********************/ /** *@attention -*Touch button is through the body's capacitive characteristics, -*there is a charge discharge circuit inside the. When the hands touch, -*the charge and discharge time will be slow. -*Because of the different hardware, each pad needs to be calibrated at the factory. -*We use touch_pad_read to determine factory parament. -*/ + *Touch button is through the body's capacitive characteristics, + *there is a charge discharge circuit inside the. When the hands touch, + *the charge and discharge time will be slow. + *Because of the different hardware, each pad needs to be calibrated at the factory. + *We use touch_pad_read to determine factory parameters. + */ /** - *----------EXAMPLE TO CONIFGURE GPIO AS OUTPUT ------------ * + *----------EXAMPLE TO CONFIGURE GPIO AS OUTPUT ------------ * * @code{c} * touch_pad_init(); * void taskA(void* arg) * { * for(;;){ * vtaskDelay(20/portTICK_PERIOD_MS); - * ets_printf("tocuch pad value %u\n",touch_pad_read(0));//Take the touched status and untouched status value + * ets_printf("touch pad value %u\n",touch_pad_read(0));//Take the touched status and untouched status value * } * } * @endcode @@ -124,22 +124,17 @@ esp_err_t touch_pad_isr_handler_register(uint32_t touch_intr_num, void(*fn)(void /** *----------EXAMPLE TO SET ISR HANDLER ---------------------- * @code{c} - * //the first parameter is INUM, you can pick one form interrupt level 1/2 which is not used by the system. - * touch_pad_isr_handler_register(19,rtc_intr,NULL); //hook the isr handler for TouchPad interrupt + * touch_pad_isr_handler_register(rtc_intr,NULL, 0, NULL) //hook the isr handler for TouchPad interrupt * @endcode - * @note - * 1. user should arrange the INUMs that used, better not to use a same INUM for different interrupt. - * 2. do not pick the INUM that already occupied by the system. - * 3. refer to soc.h to check which INUMs that can be used. */ /** *----------EXAMPLE TO USE TOUCH_PAD------------ * * @code{c} * touch_pad_init();//only init one time * touch_pad_config(0,300);//set the intr threshold,use touch_pad_read to determine this threshold - * touch_pad_isr_handler_register(19,rtc_intr,NULL) + * touch_pad_isr_handler_register(rtc_intr,NULL, 0, NULL) * #include "esp_attr.h" - * void IRAM_ATTR rtc_intr(void * arg) + * void rtc_intr(void * arg) * { * uint32_t pad_intr = READ_PERI_REG(SARADC_SAR_TOUCH_CTRL2_REG) & 0x3ff; * uint8_t i = 0; diff --git a/components/driver/rtc_module.c b/components/driver/rtc_module.c index 5da738b452..ab6112d4f2 100644 --- a/components/driver/rtc_module.c +++ b/components/driver/rtc_module.c @@ -264,15 +264,10 @@ esp_err_t rtc_gpio_pulldown_dis(gpio_num_t gpio_num) /*--------------------------------------------------------------- Touch Pad ---------------------------------------------------------------*/ -esp_err_t touch_pad_isr_handler_register(uint32_t touch_intr_num, void(*fn)(void *), void *arg) +esp_err_t touch_pad_isr_handler_register(void(*fn)(void *), void *arg, int intr_alloc_flags, touch_isr_handle_t *handle) { RTC_MODULE_CHECK(fn, "Touch_Pad ISR null", ESP_ERR_INVALID_ARG); - ESP_INTR_DISABLE(touch_intr_num); - intr_matrix_set(xPortGetCoreID(), ETS_RTC_CORE_INTR_SOURCE, touch_intr_num); - xt_set_interrupt_handler(touch_intr_num, fn, arg); - ESP_INTR_ENABLE(touch_intr_num); - - return ESP_OK; + return esp_intr_alloc(ETS_RTC_CORE_INTR_SOURCE, intr_alloc_flags, fn, arg, handle); } static esp_err_t touch_pad_get_io_num(touch_pad_t touch_num, gpio_num_t *gpio_num) diff --git a/components/esp32/lib b/components/esp32/lib index 5902a2229e..3a412c08af 160000 --- a/components/esp32/lib +++ b/components/esp32/lib @@ -1 +1 @@ -Subproject commit 5902a2229e5371aeea45c09e63ea5e233b58750f +Subproject commit 3a412c08af1ace47a58d1f8722a8fed5b8d3b944 diff --git a/tools/unit-test-app/sdkconfig b/tools/unit-test-app/sdkconfig index 9e5be636de..1b9db99518 100644 --- a/tools/unit-test-app/sdkconfig +++ b/tools/unit-test-app/sdkconfig @@ -93,6 +93,11 @@ CONFIG_SYSTEM_EVENT_QUEUE_SIZE=32 CONFIG_SYSTEM_EVENT_TASK_STACK_SIZE=2048 CONFIG_MAIN_TASK_STACK_SIZE=4096 CONFIG_NEWLIB_STDOUT_ADDCR=y +CONFIG_CONSOLE_UART_DEFAULT=y +# CONFIG_CONSOLE_UART_CUSTOM is not set +# CONFIG_CONSOLE_UART_NONE is not set +CONFIG_CONSOLE_UART_NUM=0 +CONFIG_CONSOLE_UART_BAUDRATE=115200 CONFIG_ULP_COPROC_ENABLED=y CONFIG_ULP_COPROC_RESERVE_MEM=512 # CONFIG_ESP32_PANIC_PRINT_HALT is not set From baa6a477c59dce2e3d5fffa2945ef9c3ac0c93a2 Mon Sep 17 00:00:00 2001 From: Jeroen Domburg Date: Thu, 8 Dec 2016 14:42:00 +0800 Subject: [PATCH 7/7] Fix example code, add chapter to docs about interrupts and multicore issues --- components/driver/include/driver/rmt.h | 5 ++-- components/driver/include/driver/uart.h | 3 ++- docs/api/intr_alloc.rst | 27 ++++++++++++++++++++++ examples/13_timer_group/main/timer_group.c | 4 ++-- examples/16_pcnt/main/pcnt_test.c | 2 +- 5 files changed, 34 insertions(+), 7 deletions(-) diff --git a/components/driver/include/driver/rmt.h b/components/driver/include/driver/rmt.h index 1c136689b7..24df1ac8ed 100644 --- a/components/driver/include/driver/rmt.h +++ b/components/driver/include/driver/rmt.h @@ -568,15 +568,14 @@ esp_err_t rmt_config(rmt_config_t* rmt_param); * @brief register RMT interrupt handler, the handler is an ISR. * * The handler will be attached to the same CPU core that this function is running on. - * @note - * If you already called rmt_driver_install to use system RMT driver, + * @note If you already called rmt_driver_install to use system RMT driver, * please do not register ISR handler again. * * @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. - * @param If non-zero, a handle to later clean up the ISR gets stored here. + * @param handle If non-zero, a handle to later clean up the ISR gets stored here. * * @return * - ESP_OK Success diff --git a/components/driver/include/driver/uart.h b/components/driver/include/driver/uart.h index dd69dce275..951ada929a 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -381,7 +381,8 @@ esp_err_t uart_isr_register(uart_port_t uart_num, void (*fn)(void*), void * arg, /** - * @brief Free UART interrupt handler registered by uart_isr_register. + * @brief Free UART interrupt handler registered by uart_isr_register. Must be called on the same core as + * uart_isr_register was called. * * @param uart_num UART_NUM_0, UART_NUM_1 or UART_NUM_2 * diff --git a/docs/api/intr_alloc.rst b/docs/api/intr_alloc.rst index e4e9bdb4ad..4d2f21abac 100644 --- a/docs/api/intr_alloc.rst +++ b/docs/api/intr_alloc.rst @@ -29,7 +29,33 @@ interrupt for DevA is still pending, but because the int line never went low (De even when the int for DevB was cleared) the interrupt is never serviced.) +Multicore issues +---------------- +Peripherals that can generate interrupts can be divided in two types: external peripherals, outside the Xtensa +cores in the ESP32, and internal peripherals, inside the ESP32. Interrupt handling differs slightly between +these two types of peripherals. + +Each Xtensa core has its own set of internal peripherals: three timer comparators, a performance monitor and two +software interrupts. These peripherals can only be configured from the core they are associated with. When +generating an interrupt, the interrupt they generate is hard-wired to their associated core; it's not possible +to have e.g. an internal timer comparator of one core generate an interrupt on another core. That is why these +sources can only be managed using a task running on that specific core. Internal interrupt sources are still +allocatable using esp_intr_alloc as normal, but they cannot be shared and will always have a fixed interrupt +level (namely, the one associated in hardware with the peripheral). Internal interrupt sources are defined +in esp_intr_alloc.h as ETS_INTERNAL_*_INTR_SOURCE. + +The remaining interrupt slots in both cores are wired to an interrupt multiplexer, which can be used to +route any external interrupt source to any of these interrupt slots. Allocating an external interrupt will always +allocate it on the core that does the allocation, and freeing the interrupt should always happen on the same +core. Disabling and enabling the interrupt from another core is allowed, however. External interrupts can +share an interrupt slot bu passing ESP_INTR_FLAG_SHARED as a flag to esp_intr_alloc. External interrupt sources +are defined in soc/soc.h as ETS_*_INTR_SOURCE. + +Care should be taken when allocating an interrupt using a task not pinned to a certain core; while running +code not in a critical secion, these tasks can migrate between cores at any moment, possibly making an +interrupt operation fail because of the reasons mentioned above. It is advised to always use +xTaskCreatePinnedToCore with a specific CoreID argument to create tasks that will handle interrupts. Application Example ------------------- @@ -58,6 +84,7 @@ Macros .. doxygendefine:: ESP_INTR_FLAG_SHARED .. doxygendefine:: ESP_INTR_FLAG_EDGE .. doxygendefine:: ESP_INTR_FLAG_IRAM +.. doxygendefine:: ESP_INTR_FLAG_INTRDISABLED Type Definitions ^^^^^^^^^^^^^^^^ diff --git a/examples/13_timer_group/main/timer_group.c b/examples/13_timer_group/main/timer_group.c index f61f39b2c5..9db471054d 100644 --- a/examples/13_timer_group/main/timer_group.c +++ b/examples/13_timer_group/main/timer_group.c @@ -157,7 +157,7 @@ void tg0_timer0_init() /*Enable timer interrupt*/ timer_enable_intr(timer_group, timer_idx); /*Set ISR handler*/ - timer_isr_register(timer_group, timer_idx, timer_group0_isr, (void*) timer_idx, ESP_INTR_FLAG_IRAM); + timer_isr_register(timer_group, timer_idx, timer_group0_isr, (void*) timer_idx, ESP_INTR_FLAG_IRAM, NULL); /*Start timer counter*/ timer_start(timer_group, timer_idx); } @@ -187,7 +187,7 @@ void tg0_timer1_init() /*Enable timer interrupt*/ timer_enable_intr(timer_group, timer_idx); /*Set ISR handler*/ - timer_isr_register(timer_group, timer_idx, timer_group0_isr, (void*) timer_idx, ESP_INTR_FLAG_IRAM); + timer_isr_register(timer_group, timer_idx, timer_group0_isr, (void*) timer_idx, ESP_INTR_FLAG_IRAM, NULL); /*Start timer counter*/ timer_start(timer_group, timer_idx); } diff --git a/examples/16_pcnt/main/pcnt_test.c b/examples/16_pcnt/main/pcnt_test.c index 31d8f28870..cc844dcd81 100644 --- a/examples/16_pcnt/main/pcnt_test.c +++ b/examples/16_pcnt/main/pcnt_test.c @@ -176,7 +176,7 @@ static void pcnt_init(void) /*Reset counter value*/ pcnt_counter_clear(PCNT_TEST_UNIT); /*Register ISR handler*/ - pcnt_isr_register(pcnt_intr_handler, NULL, 0); + pcnt_isr_register(pcnt_intr_handler, NULL, 0, NULL); /*Enable interrupt for PCNT unit*/ pcnt_intr_enable(PCNT_TEST_UNIT); /*Resume counting*/