diff --git a/tests/kernel/interrupt/src/interrupt_util.h b/tests/kernel/interrupt/src/interrupt_util.h index 11e39992e00..598781cb34b 100644 --- a/tests/kernel/interrupt/src/interrupt_util.h +++ b/tests/kernel/interrupt/src/interrupt_util.h @@ -33,9 +33,12 @@ static inline u32_t get_available_nvic_line(u32_t initial_offset) NVIC_SetPendingIRQ(i); if (NVIC_GetPendingIRQ(i)) { - /* If the NVIC line is pending, it is - * guaranteed that it is implemented. + /* + * If the NVIC line is pending, it is + * guaranteed that it is implemented; clear the + * line and return the NVIC line number. */ + NVIC_ClearPendingIRQ(i); break; } } diff --git a/tests/kernel/interrupt/src/nested_irq.c b/tests/kernel/interrupt/src/nested_irq.c index ad5ca366298..434d05b7d08 100644 --- a/tests/kernel/interrupt/src/nested_irq.c +++ b/tests/kernel/interrupt/src/nested_irq.c @@ -1,4 +1,5 @@ /* + * Copyright (c) 2020 Stephanos Ioannidis * Copyright (c) 2018 Intel Corporation * * SPDX-License-Identifier: Apache-2.0 @@ -7,120 +8,122 @@ #include #include "interrupt_util.h" -#define DURATION 5 -struct k_timer timer; +#define DURATION 5 -/* This tests uses two IRQ lines, selected within the range of IRQ lines - * available on the target SOC the test executes on (and starting from - * the maximum available IRQ line index) - */ -#define IRQ_LINE(offset) (CONFIG_NUM_IRQS - ((offset) + 1)) +#define ISR0_TOKEN 0xDEADBEEF +#define ISR1_TOKEN 0xCAFEBABE -#define ISR0_OFFSET 1 -#define ISR1_OFFSET 2 - -/* Keeping isr0 to be lowest priority than system timer - * so that it can be interrupted by timer triggered. - * In NRF5, RTC system timer is of priority 1 and - * in all other architectures, system timer is considered - * to be in priority 0. +/* + * This test uses two IRQ lines selected within the range of available IRQs on + * the target SoC. These IRQs are platform and interrupt controller-specific, + * and must be specified for every supported platform. + * + * In terms of priority, the IRQ1 is triggered from the ISR of the IRQ0; + * therefore, the priority of IRQ1 must be greater than that of the IRQ0. */ #if defined(CONFIG_CPU_CORTEX_M) -u32_t irq_line_0; -u32_t irq_line_1; -#define ISR0_PRIO 2 -#define ISR1_PRIO 1 +/* + * For Cortex-M NVIC, unused and available IRQs are automatically detected when + * when the test is run. + */ +#define IRQ0_PRIO 2 +#define IRQ1_PRIO 1 #else -#define ISR0_PRIO 1 -#define ISR1_PRIO 0 -#endif /* CONFIG_CPU_CORTEX_M */ +/* + * For all the other platforms, use the last two available IRQ lines for + * testing. + */ +#define IRQ0_LINE (CONFIG_NUM_IRQS - 1) +#define IRQ1_LINE (CONFIG_NUM_IRQS - 2) -volatile u32_t new_val; -u32_t old_val = 0xDEAD; +#define IRQ0_PRIO 1 +#define IRQ1_PRIO 0 +#endif + +#ifndef NO_TRIGGER_FROM_SW +static u32_t irq_line_0; +static u32_t irq_line_1; + +static u32_t isr0_result; +static u32_t isr1_result; void isr1(void *param) { ARG_UNUSED(param); - new_val = 0xDEAD; - printk("%s ran !!\n", __func__); -} -/** - * - * triggering interrupt from the timer expiry function while isr0 - * is in busy wait - */ -#ifndef NO_TRIGGER_FROM_SW -static void handler(struct k_timer *timer) -{ - ARG_UNUSED(timer); -#if defined(CONFIG_CPU_CORTEX_M) - irq_enable(irq_line_1); - trigger_irq(irq_line_1); -#else - irq_enable(IRQ_LINE(ISR1_OFFSET)); - trigger_irq(IRQ_LINE(ISR1_OFFSET)); -#endif /* CONFIG_CPU_CORTEX_M */ + printk("isr1: Enter\n"); + + /* Set verification token */ + isr1_result = ISR1_TOKEN; + + printk("isr1: Leave\n"); } -#else -void handler(void) -{ - ztest_test_skip(); -} -#endif /* NO_TRIGGER_FROM_SW */ void isr0(void *param) { ARG_UNUSED(param); - printk("%s running !!\n", __func__); -#if defined(CONFIG_BOARD_QEMU_CORTEX_M0) - /* QEMU Cortex-M0 timer emulation appears to not capturing the - * current time accurately, resulting in erroneous busy wait - * implementation. - * - * Work-around: - * Increase busy-loop duration to ensure the timer interrupt will fire - * during the busy loop waiting. - */ - k_busy_wait(MS_TO_US(1000)); -#else - k_busy_wait(MS_TO_US(10)); -#endif - printk("%s execution completed !!\n", __func__); - zassert_equal(new_val, old_val, "Nested interrupt is not working\n"); + + printk("isr0: Enter\n"); + + /* Set verification token */ + isr0_result = ISR0_TOKEN; + + /* Trigger nested IRQ 1 */ + trigger_irq(irq_line_1); + + /* Wait for interrupt */ + k_busy_wait(MS_TO_US(DURATION)); + + /* Validate nested ISR result token */ + zassert_equal(isr1_result, ISR1_TOKEN, "isr1 did not execute"); + + printk("isr0: Leave\n"); } /** + * @brief Test interrupt nesting * - * Interrupt nesting feature allows an ISR to be preempted in mid-execution - * if a higher priority interrupt is signaled. The lower priority ISR resumes - * execution once the higher priority ISR has completed its processing. - * The expected control flow should be isr0 -> handler -> isr1 -> isr0 + * @ingroup kernel_interrupt_tests + * + * This routine tests the interrupt nesting feature, which allows an ISR to be + * preempted in mid-execution if a higher priority interrupt is signaled. The + * lower priority ISR resumes execution once the higher priority ISR has + * completed its processing. + * + * The expected control flow for this test is as follows: + * + * 1. [thread] Trigger IRQ 0 (lower priority) + * 2. [isr0] Set ISR 0 result token and trigger IRQ 1 (higher priority) + * 3. [isr1] Set ISR 1 result token and return + * 4. [isr0] Validate ISR 1 result token and return + * 5. [thread] Validate ISR 0 result token */ -#ifndef NO_TRIGGER_FROM_SW void test_nested_isr(void) { + /* Resolve test IRQ line numbers */ #if defined(CONFIG_CPU_CORTEX_M) irq_line_0 = get_available_nvic_line(CONFIG_NUM_IRQS); irq_line_1 = get_available_nvic_line(irq_line_0); - arch_irq_connect_dynamic(irq_line_0, ISR0_PRIO, isr0, NULL, 0); - arch_irq_connect_dynamic(irq_line_1, ISR1_PRIO, isr1, NULL, 0); #else - IRQ_CONNECT(IRQ_LINE(ISR0_OFFSET), ISR0_PRIO, isr0, NULL, 0); - IRQ_CONNECT(IRQ_LINE(ISR1_OFFSET), ISR1_PRIO, isr1, NULL, 0); -#endif /* CONFIG_CPU_CORTEX_M */ + irq_line_0 = IRQ0_LINE; + irq_line_1 = IRQ1_LINE; +#endif - k_timer_init(&timer, handler, NULL); - k_timer_start(&timer, DURATION, K_NO_WAIT); + /* Connect and enable test IRQs */ + arch_irq_connect_dynamic(irq_line_0, IRQ0_PRIO, isr0, NULL, 0); + arch_irq_connect_dynamic(irq_line_1, IRQ1_PRIO, isr1, NULL, 0); -#if defined(CONFIG_CPU_CORTEX_M) irq_enable(irq_line_0); - trigger_irq(irq_line_0); -#else - irq_enable(IRQ_LINE(ISR0_OFFSET)); - trigger_irq(IRQ_LINE(ISR0_OFFSET)); -#endif /* CONFIG_CPU_CORTEX_M */ + irq_enable(irq_line_1); + /* Trigger test IRQ 0 */ + trigger_irq(irq_line_0); + + /* Wait for interrupt */ + k_busy_wait(MS_TO_US(DURATION)); + + /* Validate ISR result token */ + zassert_equal(isr0_result, ISR0_TOKEN, "isr0 did not execute"); } #else void test_nested_isr(void)