From d3dcbb829211e9c3531acee6a175277db1424e1a Mon Sep 17 00:00:00 2001 From: Graham Sanderson Date: Tue, 7 Jun 2022 14:27:38 -0500 Subject: [PATCH] Add new user_irq claim APIs to make it easier for independent code using them to interoperate (#854) --- .../include/hardware/platform_defs.h | 1 + .../hardware_irq/include/hardware/irq.h | 63 +++++++++++++++++++ src/rp2_common/hardware_irq/irq.c | 31 +++++++++ .../pico_stdio_usb/include/pico/stdio_usb.h | 4 +- src/rp2_common/pico_stdio_usb/stdio_usb.c | 19 ++++-- 5 files changed, 112 insertions(+), 6 deletions(-) diff --git a/src/rp2040/hardware_regs/include/hardware/platform_defs.h b/src/rp2040/hardware_regs/include/hardware/platform_defs.h index 08c7159..21f77ca 100644 --- a/src/rp2040/hardware_regs/include/hardware/platform_defs.h +++ b/src/rp2040/hardware_regs/include/hardware/platform_defs.h @@ -21,6 +21,7 @@ #define NUM_DMA_CHANNELS _u(12) #define NUM_DMA_TIMERS _u(4) #define NUM_IRQS _u(32) +#define NUM_USER_IRQS _u(6) #define NUM_PIOS _u(2) #define NUM_PIO_STATE_MACHINES _u(4) #define NUM_PWM_SLICES _u(8) diff --git a/src/rp2_common/hardware_irq/include/hardware/irq.h b/src/rp2_common/hardware_irq/include/hardware/irq.h index f4451b8..4f83466 100644 --- a/src/rp2_common/hardware_irq/include/hardware/irq.h +++ b/src/rp2_common/hardware_irq/include/hardware/irq.h @@ -289,6 +289,69 @@ void irq_set_pending(uint num); * \note This is an internal method and user should generally not call it. */ void irq_init_priorities(void); + +/*! \brief Claim ownership of a user IRQ on the calling core + * \ingroup hardware_irq + * + * User IRQs are numbered 26-31 and are not connected to any hardware, but can be triggered by \ref irq_set_pending. + * + * \note User IRQs are a core local feature; they cannot be used to communicate between cores. Therfore all functions + * dealing with Uer IRQs affect only the calling core + * + * This method explicitly claims ownership of a user IRQ, so other code can know it is being used. + * + * \param irq_num the user IRQ to claim + */ +void user_irq_claim(uint irq_num); + +/*! \brief Mark a user IRQ as no longer used on the calling core + * \ingroup hardware_irq + * + * User IRQs are numbered 26-31 and are not connected to any hardware, but can be triggered by \ref irq_set_pending. + * + * \note User IRQs are a core local feature; they cannot be used to communicate between cores. Therfore all functions + * dealing with Uer IRQs affect only the calling core + * + * This method explicitly releases ownership of a user IRQ, so other code can know it is free to use. + * + * \note it is customary to have disabled the irq and removed the handler prior to calling this method. + * + * \param irq_num the irq irq_num to unclaim + */ +void user_irq_unclaim(uint irq_num); + +/*! \brief Claim ownership of a free user IRQ on the calling core + * \ingroup hardware_irq + * + * User IRQs are numbered 26-31 and are not connected to any hardware, but can be triggered by \ref irq_set_pending. + * + * \note User IRQs are a core local feature; they cannot be used to communicate between cores. Therfore all functions + * dealing with Uer IRQs affect only the calling core + * + * This method explicitly claims ownership of an unused user IRQ if there is one, so other code can know it is being used. + * + * \param required if true the function will panic if none are available + * \return the user IRQ number or -1 if required was false, and none were free + */ +int user_irq_claim_unused(bool required); + +/* +*! \brief Check if a user IRQ is in use on the calling core + * \ingroup hardware_irq + * + * User IRQs are numbered 26-31 and are not connected to any hardware, but can be triggered by \ref irq_set_pending. + * + * \note User IRQs are a core local feature; they cannot be used to communicate between cores. Therfore all functions + * dealing with Uer IRQs affect only the calling core + * + * \param irq_num the irq irq_num + * \return true if the irq_num is claimed, false otherwise + * \sa user_irq_claim + * \sa user_irq_unclaim + * \sa user_irq_claim_unused + */ +bool user_irq_is_claimed(uint irq_num); + #ifdef __cplusplus } #endif diff --git a/src/rp2_common/hardware_irq/irq.c b/src/rp2_common/hardware_irq/irq.c index def098b..170fcf1 100644 --- a/src/rp2_common/hardware_irq/irq.c +++ b/src/rp2_common/hardware_irq/irq.c @@ -8,12 +8,15 @@ #include "hardware/regs/m0plus.h" #include "hardware/platform_defs.h" #include "hardware/structs/scb.h" +#include "hardware/claim.h" #include "pico/mutex.h" #include "pico/assert.h" extern void __unhandled_user_irq(void); +static uint8_t user_irq_claimed[NUM_CORES]; + static inline irq_handler_t *get_vtable(void) { return (irq_handler_t *) scb_hw->vtor; } @@ -410,3 +413,31 @@ void irq_init_priorities() { } #endif } + +#define FIRST_USER_IRQ (NUM_IRQS - NUM_USER_IRQS) + +static uint get_user_irq_claim_index(uint irq_num) { + invalid_params_if(IRQ, irq_num < FIRST_USER_IRQ || irq_num >= NUM_IRQS); + // we count backwards from the last, to match the existing hard coded uses of user IRQs in the SDK which were previously using 31 + static_assert(NUM_IRQS - FIRST_USER_IRQ <= 8, ""); // we only use a single byte's worth of claim bits today. + return NUM_IRQS - irq_num - 1u; +} + +void user_irq_claim(uint irq_num) { + hw_claim_or_assert(&user_irq_claimed[get_core_num()], get_user_irq_claim_index(irq_num), "User IRQ is already claimed"); +} + +void user_irq_unclaim(uint irq_num) { + hw_claim_clear(&user_irq_claimed[get_core_num()], get_user_irq_claim_index(irq_num)); +} + +int user_irq_claim_unused(bool required) { + int bit = hw_claim_unused_from_range(&user_irq_claimed[get_core_num()], required, 0, NUM_USER_IRQS - 1, "No user IRQs are available"); + if (bit >= 0) bit = (int)NUM_IRQS - bit - 1; + return bit; +} + +bool user_irq_is_claimed(uint irq_num) { + return hw_is_claimed(&user_irq_claimed[get_core_num()], get_user_irq_claim_index(irq_num)); +} + diff --git a/src/rp2_common/pico_stdio_usb/include/pico/stdio_usb.h b/src/rp2_common/pico_stdio_usb/include/pico/stdio_usb.h index c1ace03..8ac8244 100644 --- a/src/rp2_common/pico_stdio_usb/include/pico/stdio_usb.h +++ b/src/rp2_common/pico_stdio_usb/include/pico/stdio_usb.h @@ -39,9 +39,9 @@ #define PICO_STDIO_USB_TASK_INTERVAL_US 1000 #endif -// PICO_CONFIG: PICO_STDIO_USB_LOW_PRIORITY_IRQ, low priority (non hardware) IRQ number to claim for tud_task() background execution, default=31, advanced=true, group=pico_stdio_usb +// PICO_CONFIG: PICO_STDIO_USB_LOW_PRIORITY_IRQ, Explicit User IRQ number to claim for tud_task() background execution instead of letting the implementation pick a free one dynamically (deprecated), advanced=true, group=pico_stdio_usb #ifndef PICO_STDIO_USB_LOW_PRIORITY_IRQ -#define PICO_STDIO_USB_LOW_PRIORITY_IRQ 31 +// this variable is no longer set by default (one is claimed dynamically), but will be respected if specified #endif // PICO_CONFIG: PICO_STDIO_USB_ENABLE_RESET_VIA_BAUD_RATE, Enable/disable resetting into BOOTSEL mode if the host sets the baud rate to a magic value (PICO_STDIO_USB_RESET_MAGIC_BAUD_RATE), type=bool, default=1, group=pico_stdio_usb diff --git a/src/rp2_common/pico_stdio_usb/stdio_usb.c b/src/rp2_common/pico_stdio_usb/stdio_usb.c index 913d606..df3920f 100644 --- a/src/rp2_common/pico_stdio_usb/stdio_usb.c +++ b/src/rp2_common/pico_stdio_usb/stdio_usb.c @@ -13,7 +13,13 @@ #include "pico/mutex.h" #include "hardware/irq.h" -static_assert(PICO_STDIO_USB_LOW_PRIORITY_IRQ > RTC_IRQ, ""); // note RTC_IRQ is currently the last one +#ifdef PICO_STDIO_USB_LOW_PRIORITY_IRQ +static_assert(PICO_STDIO_USB_LOW_PRIORITY_IRQ >= NUM_IRQS - NUM_USER_IRQS, ""); +#define low_priority_irq_num PICO_STDIO_USB_LOW_PRIORITY_IRQ +#else +static uint8_t low_priority_irq_num; +#endif + static mutex_t stdio_usb_mutex; static void low_priority_worker_irq(void) { @@ -27,7 +33,7 @@ static void low_priority_worker_irq(void) { } static int64_t timer_task(__unused alarm_id_t id, __unused void *user_data) { - irq_set_pending(PICO_STDIO_USB_LOW_PRIORITY_IRQ); + irq_set_pending(low_priority_irq_num); return PICO_STDIO_USB_TASK_INTERVAL_US; } @@ -96,8 +102,13 @@ bool stdio_usb_init(void) { // initialize TinyUSB tusb_init(); - irq_set_exclusive_handler(PICO_STDIO_USB_LOW_PRIORITY_IRQ, low_priority_worker_irq); - irq_set_enabled(PICO_STDIO_USB_LOW_PRIORITY_IRQ, true); +#ifdef PICO_STDIO_USB_LOW_PRIORITY_IRQ + user_irq_claim(PICO_STDIO_USB_LOW_PRIORITY_IRQ); +#else + low_priority_irq_num = (uint8_t) user_irq_claim_unused(true); +#endif + irq_set_exclusive_handler(low_priority_irq_num, low_priority_worker_irq); + irq_set_enabled(low_priority_irq_num, true); mutex_init(&stdio_usb_mutex); bool rc = add_alarm_in_us(PICO_STDIO_USB_TASK_INTERVAL_US, timer_task, NULL, true);