diff --git a/arch/arm/core/irq_manage.c b/arch/arm/core/irq_manage.c index dfbea870127e1..b97072c407017 100644 --- a/arch/arm/core/irq_manage.c +++ b/arch/arm/core/irq_manage.c @@ -132,6 +132,25 @@ void z_irq_spurious(void *unused) __reserved(); } +static int z_arm_irq_current_prio(void) +{ + u32_t isr_vector_num = __get_IPSR() & IPSR_ISR_Msk; + s32_t irq_type; + + if (isr_vector_num == 0) { + return INT_MAX; + } + + irq_type = ((s32_t)isr_vector_num - 16); + + return (NVIC_GetPriority((IRQn_Type)irq_type) & 0xFF); +} + +bool z_arm_irq_can_preempt(u32_t irq_line) +{ + return z_arm_irq_current_prio() > NVIC_GetPriority((IRQn_Type)irq_line); +} + /* FIXME: IRQ direct inline functions have to be placed here and not in * arch/cpu.h as inline functions due to nasty circular dependency between * arch/cpu.h and kernel_structs.h; the inline functions typically need to diff --git a/boards/posix/nrf52_bsim/board_irq.h b/boards/posix/nrf52_bsim/board_irq.h index 2b1174c0a5b35..5b74b522934ec 100644 --- a/boards/posix/nrf52_bsim/board_irq.h +++ b/boards/posix/nrf52_bsim/board_irq.h @@ -10,6 +10,7 @@ #include "sw_isr_table.h" #include "zephyr/types.h" +#include "NRF_regs.h" #ifdef __cplusplus extern "C" { @@ -18,6 +19,8 @@ extern "C" { void z_isr_declare(unsigned int irq_p, int flags, void isr_p(void *), void *isr_param_p); void z_irq_priority_set(unsigned int irq, unsigned int prio, u32_t flags); +bool z_arm_irq_can_preempt(u32_t irq_line); +unsigned int NVIC_GetPriority(IRQn_Type IRQn); /** * Configure a static interrupt. diff --git a/boards/posix/nrf52_bsim/irq_handler.c b/boards/posix/nrf52_bsim/irq_handler.c index 7bdd2a403a9ec..67c69562f6763 100644 --- a/boards/posix/nrf52_bsim/irq_handler.c +++ b/boards/posix/nrf52_bsim/irq_handler.c @@ -327,6 +327,25 @@ void z_irq_priority_set(unsigned int irq, unsigned int prio, uint32_t flags) hw_irq_ctrl_prio_set(irq, prio); } +/** + * @brief Return current context priority. + * + * @return Interrupt priority or INT_MAX if in thread mode. + */ +static int z_arm_irq_current_prio(void) +{ + if (_kernel.nested == 0) { + return INT_MAX; + } else { + return hw_irq_ctrl_get_cur_prio(); + } +} + +bool z_arm_irq_can_preempt(u32_t irq_line) +{ + return z_arm_irq_current_prio() > NVIC_GetPriority((IRQn_Type)irq_line); +} + /** * Similar to ARM's NVIC_SetPendingIRQ * set a pending IRQ from SW @@ -402,6 +421,15 @@ void NVIC_ClearPendingIRQ(IRQn_Type IRQn) hw_irq_ctrl_clear_irq(IRQn); } +/* + * Replacement for ARMs NVIC_GetPriority() + * Return the programmed priority of IRQn + */ +unsigned int NVIC_GetPriority(IRQn_Type IRQn) +{ + return hw_irq_ctrl_get_prio(IRQn); +} + /* * Very simple model of the WFE and SEV ARM instructions * which seems good enough for the Nordic controller diff --git a/include/arch/arm/cortex_m/irq.h b/include/arch/arm/cortex_m/irq.h index c365e23611d26..7b538042e513a 100644 --- a/include/arch/arm/cortex_m/irq.h +++ b/include/arch/arm/cortex_m/irq.h @@ -153,8 +153,21 @@ extern void z_irq_spurious(void *unused); extern void _isr_wrapper(void); #endif +/** + * @brief Check if current context can be preempted by given interrupt. + * + * @note Function does not check if interrupt is enabled. + * + * @param irq_line Interrupt ID. + * + * @return true if current context can be preempted by given interrupt, false + * otherwise. + */ +extern bool z_arm_irq_can_preempt(u32_t irq_line); + #endif /* _ASMLANGUAGE */ + #ifdef __cplusplus } #endif diff --git a/tests/kernel/arm_irq_vector_table/src/arm_irq_vector_table.c b/tests/kernel/arm_irq_vector_table/src/arm_irq_vector_table.c index fabcd64c84e40..ba4a4ad0b6dda 100644 --- a/tests/kernel/arm_irq_vector_table/src/arm_irq_vector_table.c +++ b/tests/kernel/arm_irq_vector_table/src/arm_irq_vector_table.c @@ -68,6 +68,10 @@ struct k_sem sem[3]; void isr0(void) { printk("%s ran!\n", __func__); + /* Test which interrupt can preempt current context */ + zassert_false(z_arm_irq_can_preempt(0 + _ISR_OFFSET), ""); + zassert_false(z_arm_irq_can_preempt(1 + _ISR_OFFSET), ""); + zassert_false(z_arm_irq_can_preempt(2 + _ISR_OFFSET), ""); k_sem_give(&sem[0]); _IntExit(); } @@ -82,6 +86,10 @@ void isr0(void) void isr1(void) { printk("%s ran!\n", __func__); + /* Test which interrupt can preempt current context */ + zassert_true(z_arm_irq_can_preempt(0 + _ISR_OFFSET), ""); + zassert_false(z_arm_irq_can_preempt(1 + _ISR_OFFSET), ""); + zassert_false(z_arm_irq_can_preempt(2 + _ISR_OFFSET), ""); k_sem_give(&sem[1]); _IntExit(); } @@ -96,6 +104,9 @@ void isr1(void) void isr2(void) { printk("%s ran!\n", __func__); + zassert_true(z_arm_irq_can_preempt(0 + _ISR_OFFSET), ""); + zassert_true(z_arm_irq_can_preempt(1 + _ISR_OFFSET), ""); + zassert_false(z_arm_irq_can_preempt(2 + _ISR_OFFSET), ""); k_sem_give(&sem[2]); _IntExit(); } @@ -116,19 +127,29 @@ void isr2(void) * NVIC_SetPendingIRQ(), to trigger the pending interrupt. And we check * that the corresponding interrupt handler is getting called or not. * - * @see irq_enable(), z_irq_priority_set(), NVIC_SetPendingIRQ() + * Addtionally, z_arm_irq_can_preempt() is tested in the thread mode and + * interrupt context. + * + * @see irq_enable(), z_irq_priority_set(), NVIC_SetPendingIRQ(), + * z_arm_irq_can_preempt() * */ void test_arm_irq_vector_table(void) { printk("Test Cortex-M IRQs installed directly in the vector table\n"); + for (int ii = 0; ii < 3; ii++) { irq_enable(_ISR_OFFSET + ii); - z_irq_priority_set(_ISR_OFFSET + ii, 0, 0); + z_irq_priority_set(_ISR_OFFSET + ii, ii, 0); k_sem_init(&sem[ii], 0, UINT_MAX); } + /* Test that context can be preempted by each interrupt */ + zassert_true(z_arm_irq_can_preempt(0 + _ISR_OFFSET), ""); + zassert_true(z_arm_irq_can_preempt(1 + _ISR_OFFSET), ""); + zassert_true(z_arm_irq_can_preempt(2 + _ISR_OFFSET), ""); + zassert_true((k_sem_take(&sem[0], K_NO_WAIT) || k_sem_take(&sem[1], K_NO_WAIT) || k_sem_take(&sem[2], K_NO_WAIT)), NULL);