diff --git a/components/driver/gpio.c b/components/driver/gpio.c index f8694597a8..a78239eae7 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_isr_handle_t *handle) { 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, 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 de7525bd5f..fba013fe8b 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); /** @@ -343,19 +347,18 @@ 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. - * - * @note - * Note that the handler function MUST be defined with attribution of "IRAM_ATTR". - * + * @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 ; * - 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, gpio_isr_handle_t *handle); @@ -415,7 +418,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 +431,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 +444,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..fb97c6c011 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 @@ -257,20 +259,20 @@ 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. - * @note - * Note that the handler function MUST be defined with attribution of "IRAM_ATTR". + * @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. * @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 * - 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, ledc_isr_handle_t *handle); /** * @brief configure LEDC settings @@ -398,13 +400,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..f5a10581c0 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 * @@ -213,21 +216,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. + * @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. */ -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, 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 50a5c743dd..24df1ac8ed 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. * @@ -566,27 +568,32 @@ 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, + * @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. + * @param handle 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(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, 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. @@ -727,7 +734,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,25 +754,20 @@ 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} * #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/driver/include/driver/timer.h b/components/driver/include/driver/timer.h index c0ad7116e4..134fd504fb 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" { @@ -94,12 +95,19 @@ 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; + +/** + * @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. * @@ -245,21 +253,20 @@ 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. + * 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. @@ -268,7 +275,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, timer_isr_handle_t *handle); /** @brief Initializes and configure the timer. * 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/include/driver/uart.h b/components/driver/include/driver/uart.h index 1ddfcad9fa..951ada929a 100644 --- a/components/driver/include/driver/uart.h +++ b/components/driver/include/driver/uart.h @@ -366,21 +366,31 @@ 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. * * @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 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 + * + * @return + * - ESP_OK Success + * - ESP_FAIL Parameter error + */ +esp_err_t uart_isr_free(uart_port_t uart_num); /** * @brief Set UART pin number @@ -461,14 +471,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 +744,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..8d7ef89e7b 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, ledc_isr_handle_t *handle) { + 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, handle); 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..9618d1599e 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_isr_handle_t *handle) { 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, handle); } diff --git a/components/driver/rmt.c b/components/driver/rmt.c index f23fa257d0..8f3816ec9a 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" @@ -45,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); \ @@ -472,17 +474,21 @@ 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, 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); - 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, handle); portEXIT_CRITICAL(&rmt_spinlock); - return ESP_OK; + 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) @@ -616,10 +622,10 @@ 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 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 +633,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 +657,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_intr_handle); 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/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/driver/timer.c b/components/driver/timer.c index b305a41468..8572753794 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,38 @@ 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_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); - ESP_INTR_DISABLE(timer_intr_num); int intr_source = 0; + uint32_t status_reg = 0; + int mask = 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; } + status_reg = TIMG_INT_ST_TIMERS_REG(0); + mask = 1<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_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; +} + + +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 @@ -859,7 +871,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 +888,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 +913,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 +937,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 +969,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 +982,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 +993,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_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/include/esp_intr_alloc.h b/components/esp32/include/esp_intr_alloc.h new file mode 100644 index 0000000000..c1f91dd2e3 --- /dev/null +++ b/components/esp32/include/esp_intr_alloc.h @@ -0,0 +1,267 @@ +// 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 intr_handle_t to store a handle that can later be + * used to request details or free the interrupt. Can be NULL if no handle + * is required. + * + * @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid. + * ESP_ERR_NOT_FOUND No free interrupt found with the specified flags + * ESP_OK otherwise + */ +esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *arg, intr_handle_t *ret_handle); + + +/** + * @brief Allocate an interrupt with the given parameters. + * + * + * This essentially does the same as esp_intr_alloc, but allows specifying a register and mask + * combo. For shared interrupts, the handler is only called if a read from the specified + * register, ANDed with the mask, returns non-zero. By passing an interrupt status register + * address and a fitting mask, this can be used to accelerate interrupt handling in the case + * a shared interrupt is triggered; by checking the interrupt statuses first, the code can + * decide which ISRs can be skipped + * + * @param source The interrupt source. One of the ETS_*_INTR_SOURCE interrupt mux + * sources, as defined in soc/soc.h, or one of the internal + * ETS_INTERNAL_*_INTR_SOURCE sources as defined in this header. + * @param flags An ORred mask of the ESP_INTR_FLAG_* defines. These restrict the + * choice of interrupts that this routine can choose from. If this value + * is 0, it will default to allocating a non-shared interrupt of level + * 1, 2 or 3. If this is ESP_INTR_FLAG_SHARED, it will allocate a shared + * interrupt of level 1. Setting ESP_INTR_FLAG_INTRDISABLED will return + * from this function with the interrupt disabled. + * @param intrstatusreg The address of an interrupt status register + * @param intrstatusmask A mask. If a read of address intrstatusreg has any of the bits + * that are 1 in the mask set, the ISR will be called. If not, it will be + * skipped. + * @param handler The interrupt handler. Must be NULL when an interrupt of level >3 + * is requested, because these types of interrupts aren't C-callable. + * @param arg Optional argument for passed to the interrupt handler + * @param ret_handle Pointer to an intr_handle_t to store a handle that can later be + * used to request details or free the interrupt. Can be NULL if no handle + * is required. + * + * @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid. + * ESP_ERR_NOT_FOUND No free interrupt found with the specified flags + * ESP_OK otherwise + */ +esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusreg, uint32_t intrstatusmask, intr_handler_t handler, void *arg, intr_handle_t *ret_handle); + + +/** + * @brief Disable and free an interrupt. + * + * Use an interrupt handle to disable the interrupt and release the resources + * associated with it. + * + * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus + * + * @return ESP_ERR_INVALID_ARG if handle is invalid, or esp_intr_free runs on another core than + * where the interrupt is allocated on. + * ESP_OK otherwise + */ +esp_err_t esp_intr_free(intr_handle_t handle); + + +/** + * @brief Get CPU number an interrupt is tied to + * + * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus + * + * @return The core number where the interrupt is allocated + */ +int esp_intr_get_cpu(intr_handle_t handle); + +/** + * @brief Get the allocated interrupt for a certain handle + * + * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus + * + * @return The interrupt number + */ +int esp_intr_get_intno(intr_handle_t handle); + + +/** + * @brief Disable the interrupt associated with the handle + * + * @note For local interrupts (ESP_INTERNAL_* sources), this function has to be called on the + * CPU the interrupt is allocated on. Other interrupts have no such restriction. + * + * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus + * + * @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid. + * ESP_OK otherwise + */ +esp_err_t esp_intr_disable(intr_handle_t handle); + +/** + * @brief Ensable the interrupt associated with the handle + * + * @note For local interrupts (ESP_INTERNAL_* sources), this function has to be called on the + * CPU the interrupt is allocated on. Other interrupts have no such restriction. + * + * @param handle The handle, as obtained by esp_intr_alloc or esp_intr_alloc_intrstatus + * + * @return ESP_ERR_INVALID_ARG if the combination of arguments is invalid. + * ESP_OK otherwise + */ +esp_err_t esp_intr_enable(intr_handle_t handle); + + +/** + * @brief 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/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 new file mode 100644 index 0000000000..77572b1a57 --- /dev/null +++ b/components/esp32/intr_alloc.c @@ -0,0 +1,728 @@ +// 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_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 + { 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 +}; + +typedef struct shared_vector_desc_t shared_vector_desc_t; +typedef struct vector_desc_t vector_desc_t; + +struct shared_vector_desc_t { + int disabled: 1; + int source: 8; + volatile uint32_t *statusreg; + uint32_t statusmask; + intr_handler_t isr; + void *arg; + shared_vector_desc_t *next; +}; + + +#define VECDESC_FL_RESERVED (1<<0) +#define VECDESC_FL_INIRAM (1<<1) +#define VECDESC_FL_SHARED (1<<2) +#define VECDESC_FL_NONSHARED (1<<3) + +//Pack using bitfields for better memory use +struct vector_desc_t { + int flags: 16; //OR of VECDESC_FLAG_* defines + unsigned int cpu: 1; + unsigned int intno: 5; + int source: 8; //Interrupt mux flags, used when not shared + shared_vector_desc_t *shared_vec_info; //used when VECDESC_FL_SHARED + vector_desc_t *next; +}; + +struct intr_handle_data_t { + vector_desc_t *vector_desc; + shared_vector_desc_t *shared_vector_desc; +}; + + +//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. +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 cpu.intno value. +static void insert_vector_desc(vector_desc_t *to_insert) +{ + vector_desc_t *vd=vector_desc_head; + vector_desc_t *prev=NULL; + while(vd!=NULL) { + if (vd->cpu > to_insert->cpu) break; + if (vd->cpu == to_insert->cpu && vd->intno >= to_insert->intno) break; + prev=vd; + vd=vd->next; + } + if (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->cpu==cpu && vd->intno==intno) break; + vd=vd->next; + } + return vd; +} + +//Returns a vector_desc entry for an intno/cpu. +//Either returns a preexisting one or allocates a new one and inserts +//it into the list. Returns NULL on malloc fail. +static vector_desc_t *get_desc_for_int(int intno, int cpu) +{ + vector_desc_t *vd=find_desc_for_int(intno, cpu); + if (vd==NULL) { + vector_desc_t *newvd=malloc(sizeof(vector_desc_t)); + if (newvd==NULL) return NULL; + memset(newvd, 0, sizeof(vector_desc_t)); + newvd->intno=intno; + newvd->cpu=cpu; + insert_vector_desc(newvd); + return newvd; + } else { + return vd; + } +} + +esp_err_t esp_intr_mark_shared(int intno, int cpu, bool is_int_ram) +{ + if (intno>31) return ESP_ERR_INVALID_ARG; + if (cpu>=portNUM_PROCESSORS) return ESP_ERR_INVALID_ARG; + + portENTER_CRITICAL(&spinlock); + vector_desc_t *vd=get_desc_for_int(intno, cpu); + if (vd==NULL) { + portEXIT_CRITICAL(&spinlock); + return ESP_ERR_NO_MEM; + } + vd->flags=VECDESC_FL_SHARED; + if (is_int_ram) vd->flags|=VECDESC_FL_INIRAM; + portEXIT_CRITICAL(&spinlock); + + return ESP_OK; +} + +esp_err_t esp_intr_reserve(int intno, int cpu) +{ + if (intno>31) return ESP_ERR_INVALID_ARG; + if (cpu>=portNUM_PROCESSORS) return ESP_ERR_INVALID_ARG; + + portENTER_CRITICAL(&spinlock); + vector_desc_t *vd=get_desc_for_int(intno, cpu); + if (vd==NULL) { + portEXIT_CRITICAL(&spinlock); + return ESP_ERR_NO_MEM; + } + vd->flags=VECDESC_FL_RESERVED; + portEXIT_CRITICAL(&spinlock); + + return ESP_OK; +} + +//Interrupt handler table and unhandled uinterrupt routine. Duplicated +//from xtensa_intr.c... it's supposed to be private, but we need to look +//into it in order to see if someone allocated an int using +//xt_set_interrupt_handler. +typedef struct xt_handler_table_entry { + void * handler; + void * arg; +} xt_handler_table_entry; +extern xt_handler_table_entry _xt_interrupt_table[XCHAL_NUM_INTERRUPTS*portNUM_PROCESSORS]; +extern void xt_unhandled_interrupt(void * arg); + +//Returns true if handler for interrupt is not the default unhandled interrupt handler +static bool int_has_handler(int intr, int cpu) +{ + return (_xt_interrupt_table[intr*portNUM_PROCESSORS+cpu].handler != xt_unhandled_interrupt); +} + + +//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; + 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<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; + //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) { + 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<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->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); +} + + +//We use ESP_EARLY_LOG* here because this can be called before the scheduler is running. +esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusreg, uint32_t intrstatusmask, intr_handler_t handler, + void *arg, intr_handle_t *ret_handle) +{ + intr_handle_data_t *ret=NULL; + int force=-1; + ESP_EARLY_LOGV(TAG, "esp_intr_alloc_intrstatus (cpu %d): checking args", xPortGetCoreID()); + //Shared interrupts should be level-triggered. + if ((flags&ESP_INTR_FLAG_SHARED) && (flags&ESP_INTR_FLAG_EDGE)) return ESP_ERR_INVALID_ARG; + //You can't set an handler / arg for a non-C-callable interrupt. + if ((flags&ESP_INTR_FLAG_HIGH) && (handler)) return ESP_ERR_INVALID_ARG; + //Shared ints should have handler and non-processor-local source + if ((flags&ESP_INTR_FLAG_SHARED) && (!handler || source<0)) return ESP_ERR_INVALID_ARG; + //Statusreg should have a mask + if (intrstatusreg && !intrstatusmask) return ESP_ERR_INVALID_ARG; + + //Default to prio 1 for shared interrupts. Default to prio 1, 2 or 3 for non-shared interrupts. + if ((flags&ESP_INTR_FLAG_LEVELMASK)==0) { + if (flags&ESP_INTR_FLAG_SHARED) { + flags|=ESP_INTR_FLAG_LEVEL1; + } else { + flags|=ESP_INTR_FLAG_LOWMED; + } + } + ESP_EARLY_LOGV(TAG, "esp_intr_alloc_intrstatus (cpu %d): Args okay. Resulting flags 0x%X", xPortGetCoreID(), flags); + + //Check 'special' interrupt sources. These are tied to one specific interrupt, so we + //have to force get_free_int to only look at that. + if (source==ETS_INTERNAL_TIMER0_INTR_SOURCE) force=ETS_INTERNAL_TIMER0_INTR_NO; + if (source==ETS_INTERNAL_TIMER1_INTR_SOURCE) force=ETS_INTERNAL_TIMER1_INTR_NO; + if (source==ETS_INTERNAL_TIMER2_INTR_SOURCE) force=ETS_INTERNAL_TIMER2_INTR_NO; + if (source==ETS_INTERNAL_SW0_INTR_SOURCE) force=ETS_INTERNAL_SW0_INTR_NO; + if (source==ETS_INTERNAL_SW1_INTR_SOURCE) force=ETS_INTERNAL_SW1_INTR_NO; + if (source==ETS_INTERNAL_PROFILING_INTR_SOURCE) force=ETS_INTERNAL_PROFILING_INTR_NO; + + //Allocate a return handle. If we end up not needing it, we'll free it later on. + ret=malloc(sizeof(intr_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. + int intr=get_free_int(flags, cpu, force); + if (intr==-1) { + //None found. Bail out. + portEXIT_CRITICAL(&spinlock); + free(ret); + return ESP_ERR_NOT_FOUND; + } + //Get an int vector desc for int. + vector_desc_t *vd=get_desc_for_int(intr, cpu); + if (vd==NULL) { + portEXIT_CRITICAL(&spinlock); + free(ret); + return ESP_ERR_NO_MEM; + } + + //Allocate that int! + if (flags&ESP_INTR_FLAG_SHARED) { + //Populate vector entry and add to linked list. + shared_vector_desc_t *sh_vec=malloc(sizeof(shared_vector_desc_t)); + if (sh_vec==NULL) { + portEXIT_CRITICAL(&spinlock); + free(ret); + return ESP_ERR_NO_MEM; + } + memset(sh_vec, 0, sizeof(shared_vector_desc_t)); + sh_vec->statusreg=(uint32_t*)intrstatusreg; + sh_vec->statusmask=intrstatusmask; + sh_vec->isr=handler; + sh_vec->arg=arg; + sh_vec->next=vd->shared_vec_info; + sh_vec->source=source; + sh_vec->disabled=0; + vd->shared_vec_info=sh_vec; + vd->flags|=VECDESC_FL_SHARED; + //(Re-)set shared isr handler to new value. + xt_set_interrupt_handler(intr, shared_intr_isr, vd); + } else { + //Mark as unusable for other interrupt sources. This is ours now! + vd->flags=VECDESC_FL_NONSHARED; + if (handler) { + 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; + non_iram_int_mask[cpu]&=~(1<flags&=~VECDESC_FL_INIRAM; + non_iram_int_mask[cpu]|=(1<=0) { + intr_matrix_set(cpu, source, intr); + } + + //Fill return handle data. + ret->vector_desc=vd; + ret->shared_vector_desc=vd->shared_vec_info; + + //Enable int at CPU-level; + ESP_INTR_ENABLE(intr); + + //If interrupt has to be started disabled, do that now; ints won't be enabled for real until the end + //of the critical section. + if (flags&ESP_INTR_FLAG_INTRDISABLED) { + esp_intr_disable(ret); + } + + portEXIT_CRITICAL(&spinlock); + + //Fill return handle if needed, otherwise free handle. + if (ret_handle!=NULL) { + *ret_handle=ret; + } else { + free(ret); + } + + ESP_EARLY_LOGD(TAG, "Connected src %d to int %d (cpu %d)", source, intr, cpu); + return ESP_OK; +} + +esp_err_t esp_intr_alloc(int source, int flags, intr_handler_t handler, void *arg, intr_handle_t *ret_handle) +{ + /* + As an optimization, we can create a table with the possible interrupt status registers and masks for every single + source there is. We can then add code here to look up an applicable value and pass that to the + esp_intr_alloc_intrstatus function. + */ + return esp_intr_alloc_intrstatus(source, flags, 0, 0, handler, arg, ret_handle); +} + + +esp_err_t esp_intr_free(intr_handle_t handle) +{ + bool free_shared_vector=false; + if (!handle) return ESP_ERR_INVALID_ARG; + //This routine should be called from the interrupt the task is scheduled on. + if (handle->vector_desc->cpu!=xPortGetCoreID()) return ESP_ERR_INVALID_ARG; + + portENTER_CRITICAL(&spinlock); + esp_intr_disable(handle); + if (handle->vector_desc->flags&VECDESC_FL_SHARED) { + //Find and kill the shared int + shared_vector_desc_t *svd=handle->vector_desc->shared_vec_info; + shared_vector_desc_t *prevsvd=NULL; + assert(svd); //should be something in there for a shared int + while (svd!=NULL) { + if (svd==handle->shared_vector_desc) { + //Found it. Now kill it. + if (prevsvd) { + prevsvd->next=svd->next; + } else { + handle->vector_desc->shared_vec_info=svd->next; + } + free(svd); + break; + } + prevsvd=svd; + svd=svd->next; + } + //If nothing left, disable interrupt. + if (handle->vector_desc->shared_vec_info==NULL) free_shared_vector=true; + ESP_LOGV(TAG, "esp_intr_free: Deleting shared int: %s. Shared int is %s", svd?"not found or last one":"deleted", free_shared_vector?"empty now.":"still in use"); + } + + if ((handle->vector_desc->flags&VECDESC_FL_NONSHARED) || free_shared_vector) { + ESP_LOGV(TAG, "esp_intr_free: Disabling int, killing handler"); + //Reset to normal handler + xt_set_interrupt_handler(handle->vector_desc->intno, xt_unhandled_interrupt, (void*)((int)handle->vector_desc->intno)); + //Theoretically, we could free the vector_desc... not sure if that's worth the few bytes of memory + //we save.(We can also not use the same exit path for empty shared ints anymore if we delete + //the desc.) For now, just mark it as free. + handle->vector_desc->flags&=!(VECDESC_FL_NONSHARED|VECDESC_FL_RESERVED); + //Also kill non_iram mask bit. + non_iram_int_mask[handle->vector_desc->cpu]&=~(1<<(handle->vector_desc->intno)); + } + portEXIT_CRITICAL(&spinlock); + free(handle); + return ESP_OK; +} + +int esp_intr_get_intno(intr_handle_t handle) +{ + return handle->vector_desc->intno; +} + +int esp_intr_get_cpu(intr_handle_t handle) +{ + return handle->vector_desc->cpu; +} + +/* + Interrupt disabling strategy: + If the source is >=0 (meaning a muxed interrupt), we disable it by muxing the interrupt to a non-connected + interrupt. If the source is <0 (meaning an internal, per-cpu interrupt), we disable it using ESP_INTR_DISABLE. + This allows us to, for the muxed CPUs, disable an int from the other core. It also allows disabling shared + interrupts. + */ + +//Muxing an interrupt source to interrupt 6, 7, 11, 15, 16 or 29 cause the interrupt to effectively be disabled. +#define INT_MUX_DISABLED_INTNO 6 + +esp_err_t esp_intr_enable(intr_handle_t handle) +{ + if (!handle) return ESP_ERR_INVALID_ARG; + 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(intr_handle_t handle) +{ + if (!handle) return ESP_ERR_INVALID_ARG; + 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; +} + + +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/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/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 "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 */ + + +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]); +} + +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]") +{ + timer_test(0); +} + +TEST_CASE("Intr_alloc test, shared ints", "[esp32]") +{ + timer_test(ESP_INTR_FLAG_SHARED); +} diff --git a/components/ethernet/emac_main.c b/components/ethernet/emac_main.c index cbbab5149e..3dc61d809c 100644 --- a/components/ethernet/emac_main.c +++ b/components/ethernet/emac_main.c @@ -35,6 +35,7 @@ #include "esp_err.h" #include "esp_log.h" #include "esp_eth.h" +#include "esp_intr_alloc.h" #include "emac_common.h" #include "emac_desc.h" @@ -305,19 +306,16 @@ static void IRAM_ATTR emac_process_intr(void *arg) } } +//ToDo: this should only be called once because this allocates the interrupt as well. static void emac_enable_intr() { //init emac intr - REG_SET_FIELD(DPORT_PRO_EMAC_INT_MAP_REG, DPORT_PRO_EMAC_INT_MAP, ETS_EMAC_INUM); - xt_set_interrupt_handler(ETS_EMAC_INUM, emac_process_intr, NULL); - xt_ints_on(1 << ETS_EMAC_INUM); - + esp_intr_alloc(ETS_ETH_MAC_INTR_SOURCE, 0, emac_process_intr, NULL, NULL); REG_WRITE(EMAC_DMAINTERRUPT_EN_REG, EMAC_INTR_ENABLE_BIT); } static void emac_disable_intr() { - xt_ints_off(1 << ETS_EMAC_INUM); REG_WRITE(EMAC_DMAINTERRUPT_EN_REG, 0); } diff --git a/components/freertos/xtensa_intr.c b/components/freertos/xtensa_intr.c index 2f5dc3542e..4767032277 100644 --- a/components/freertos/xtensa_intr.c +++ b/components/freertos/xtensa_intr.c @@ -39,7 +39,6 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #if XCHAL_HAVE_EXCEPTIONS /* Handler table is in xtensa_intr_asm.S */ -// Todo: Make multicore - JD extern xt_exc_handler _xt_exception_table[XCHAL_EXCCAUSE_NUM*portNUM_PROCESSORS]; diff --git a/components/freertos/xtensa_intr_asm.S b/components/freertos/xtensa_intr_asm.S index 8c7ae63fdb..330b68f592 100644 --- a/components/freertos/xtensa_intr_asm.S +++ b/components/freertos/xtensa_intr_asm.S @@ -40,6 +40,9 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ------------------------------------------------------------------------------- */ + +#if XT_USE_SWPRI +/* Warning - this is not multicore-compatible. */ .data .global _xt_intdata .align 8 @@ -53,7 +56,7 @@ _xt_intdata: _xt_intenable: .word 0 /* Virtual INTENABLE */ _xt_vpri_mask: .word 0xFFFFFFFF /* Virtual priority mask */ - +#endif /* ------------------------------------------------------------------------------- @@ -124,7 +127,8 @@ _xt_exception_table: unsigned int xt_ints_on ( unsigned int mask ) Enables a set of interrupts. Does not simply set INTENABLE directly, but - computes it as a function of the current virtual priority. + computes it as a function of the current virtual priority if XT_USE_SWPRI is + enabled. Can be called from interrupt handlers. ------------------------------------------------------------------------------- */ @@ -137,7 +141,9 @@ _xt_exception_table: xt_ints_on: ENTRY0 + #if XCHAL_HAVE_INTERRUPTS +#if XT_USE_SWPRI movi a3, 0 movi a4, _xt_intdata xsr a3, INTENABLE /* Disables all interrupts */ @@ -149,6 +155,13 @@ xt_ints_on: and a5, a5, a6 /* a5 = _xt_intenable & _xt_vpri_mask */ 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 */ + wsr a2, INTENABLE /* Re-enable ints */ + mov a2, a3 /* return prev mask */ +#endif #else movi a2, 0 /* Return zero */ #endif @@ -162,7 +175,8 @@ xt_ints_on: unsigned int xt_ints_off ( unsigned int mask ) Disables a set of interrupts. Does not simply set INTENABLE directly, - but computes it as a function of the current virtual priority. + but computes it as a function of the current virtual priority if XT_USE_SWPRI is + enabled. Can be called from interrupt handlers. ------------------------------------------------------------------------------- */ @@ -176,6 +190,7 @@ xt_ints_off: ENTRY0 #if XCHAL_HAVE_INTERRUPTS +#if XT_USE_SWPRI movi a3, 0 movi a4, _xt_intdata xsr a3, INTENABLE /* Disables all interrupts */ @@ -188,6 +203,14 @@ xt_ints_off: and a5, a5, a6 /* a5 = _xt_intenable & _xt_vpri_mask */ wsr a5, INTENABLE /* Reenable interrupts */ mov a2, a3 /* Previous mask */ +#else + 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 */ #endif diff --git a/components/mbedtls/Kconfig b/components/mbedtls/Kconfig index 5a719ff33a..e6619e36c4 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 diff --git a/components/mbedtls/port/esp_bignum.c b/components/mbedtls/port/esp_bignum.c index 2fe4d920c3..5cce103ea3 100644 --- a/components/mbedtls/port/esp_bignum.c +++ b/components/mbedtls/port/esp_bignum.c @@ -32,6 +32,7 @@ #include "esp_system.h" #include "esp_log.h" #include "esp_intr.h" +#include "esp_intr_alloc.h" #include "esp_attr.h" #include "soc/dport_reg.h" @@ -59,10 +60,7 @@ static void rsa_isr_initialise() { if (op_complete_sem == NULL) { op_complete_sem = xSemaphoreCreateBinary(); - intr_matrix_set(xPortGetCoreID(), ETS_RSA_INTR_SOURCE, CONFIG_MBEDTLS_MPI_INTERRUPT_NUM); - xt_set_interrupt_handler(CONFIG_MBEDTLS_MPI_INTERRUPT_NUM, &rsa_complete_isr, NULL); - xthal_set_intclear(1 << CONFIG_MBEDTLS_MPI_INTERRUPT_NUM); - xt_ints_on(1 << CONFIG_MBEDTLS_MPI_INTERRUPT_NUM); + esp_intr_alloc(ETS_RSA_INTR_SOURCE, 0, rsa_complete_isr, NULL, NULL); } } diff --git a/components/newlib/time.c b/components/newlib/time.c index 1baa955759..363e17b3eb 100644 --- a/components/newlib/time.c +++ b/components/newlib/time.c @@ -22,6 +22,7 @@ #include #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..4d2f21abac --- /dev/null +++ b/docs/api/intr_alloc.rst @@ -0,0 +1,111 @@ +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.) + + +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 +------------------- + +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 +.. doxygendefine:: ESP_INTR_FLAG_INTRDISABLED + +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..9db471054d 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 */ @@ -88,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; @@ -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_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, 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_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, 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 5ed5fc7b1d..b8489ecb2f 100644 --- a/examples/16_pcnt/main/pcnt_test.c +++ b/examples/16_pcnt/main/pcnt_test.c @@ -41,7 +41,6 @@ #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) @@ -176,7 +175,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, NULL); /*Enable interrupt for PCNT unit*/ pcnt_intr_enable(PCNT_TEST_UNIT); /*Resume counting*/ diff --git a/tools/unit-test-app/sdkconfig b/tools/unit-test-app/sdkconfig index d82f83561d..1b9db99518 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 @@ -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 @@ -165,7 +170,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 #