Skip to content

Commit 701a93a

Browse files
stephanosiohakehuang
authored andcommitted
tests: kernel: interrupt: Rework nested interrupt test
The current nested interrupt test implementation is both buggy and fundamentally flawed because it does not trigger a higher priority interrupt from a lower priority interrupt context and relies on the system timer interrupt, which is not fully governed by the test; moreover, the current implementation does not properly validate the test results and can report success if no interrupt is triggered and serviced at all. This commit reworks this test to have the following well-defined and logical procedure: 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 The reworked test scenario ensures that the interrupt nesting works properly and any abnormal conditions are detected (e.g. interrupts not triggering at all, or ISR 1 not being nested under ISR 0). Signed-off-by: Stephanos Ioannidis <[email protected]>
1 parent ab27b1e commit 701a93a

File tree

2 files changed

+90
-84
lines changed

2 files changed

+90
-84
lines changed

tests/kernel/interrupt/src/interrupt_util.h

+5-2
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,12 @@ static inline u32_t get_available_nvic_line(u32_t initial_offset)
3333
NVIC_SetPendingIRQ(i);
3434

3535
if (NVIC_GetPendingIRQ(i)) {
36-
/* If the NVIC line is pending, it is
37-
* guaranteed that it is implemented.
36+
/*
37+
* If the NVIC line is pending, it is
38+
* guaranteed that it is implemented; clear the
39+
* line and return the NVIC line number.
3840
*/
41+
NVIC_ClearPendingIRQ(i);
3942
break;
4043
}
4144
}

tests/kernel/interrupt/src/nested_irq.c

+85-82
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
/*
2+
* Copyright (c) 2020 Stephanos Ioannidis <[email protected]>
23
* Copyright (c) 2018 Intel Corporation
34
*
45
* SPDX-License-Identifier: Apache-2.0
@@ -7,120 +8,122 @@
78
#include <ztest.h>
89
#include "interrupt_util.h"
910

10-
#define DURATION 5
11-
struct k_timer timer;
11+
#define DURATION 5
1212

13-
/* This tests uses two IRQ lines, selected within the range of IRQ lines
14-
* available on the target SOC the test executes on (and starting from
15-
* the maximum available IRQ line index)
16-
*/
17-
#define IRQ_LINE(offset) (CONFIG_NUM_IRQS - ((offset) + 1))
18-
19-
#define ISR0_OFFSET 1
20-
#define ISR1_OFFSET 2
13+
#define ISR0_TOKEN 0xDEADBEEF
14+
#define ISR1_TOKEN 0xCAFEBABE
2115

22-
/* Keeping isr0 to be lowest priority than system timer
23-
* so that it can be interrupted by timer triggered.
24-
* In NRF5, RTC system timer is of priority 1 and
25-
* in all other architectures, system timer is considered
26-
* to be in priority 0.
16+
/*
17+
* This test uses two IRQ lines selected within the range of available IRQs on
18+
* the target SoC. These IRQs are platform and interrupt controller-specific,
19+
* and must be specified for every supported platform.
20+
*
21+
* In terms of priority, the IRQ1 is triggered from the ISR of the IRQ0;
22+
* therefore, the priority of IRQ1 must be greater than that of the IRQ0.
2723
*/
2824
#if defined(CONFIG_CPU_CORTEX_M)
29-
u32_t irq_line_0;
30-
u32_t irq_line_1;
31-
#define ISR0_PRIO 2
32-
#define ISR1_PRIO 1
25+
/*
26+
* For Cortex-M NVIC, unused and available IRQs are automatically detected when
27+
* when the test is run.
28+
*/
29+
#define IRQ0_PRIO 2
30+
#define IRQ1_PRIO 1
3331
#else
34-
#define ISR0_PRIO 1
35-
#define ISR1_PRIO 0
36-
#endif /* CONFIG_CPU_CORTEX_M */
32+
/*
33+
* For all the other platforms, use the last two available IRQ lines for
34+
* testing.
35+
*/
36+
#define IRQ0_LINE (CONFIG_NUM_IRQS - 1)
37+
#define IRQ1_LINE (CONFIG_NUM_IRQS - 2)
38+
39+
#define IRQ0_PRIO 1
40+
#define IRQ1_PRIO 0
41+
#endif
42+
43+
#ifndef NO_TRIGGER_FROM_SW
44+
static u32_t irq_line_0;
45+
static u32_t irq_line_1;
3746

38-
volatile u32_t new_val;
39-
u32_t old_val = 0xDEAD;
47+
static u32_t isr0_result;
48+
static u32_t isr1_result;
4049

4150
void isr1(void *param)
4251
{
4352
ARG_UNUSED(param);
44-
new_val = 0xDEAD;
45-
printk("%s ran !!\n", __func__);
46-
}
4753

48-
/**
49-
*
50-
* triggering interrupt from the timer expiry function while isr0
51-
* is in busy wait
52-
*/
53-
#ifndef NO_TRIGGER_FROM_SW
54-
static void handler(struct k_timer *timer)
55-
{
56-
ARG_UNUSED(timer);
57-
#if defined(CONFIG_CPU_CORTEX_M)
58-
irq_enable(irq_line_1);
59-
trigger_irq(irq_line_1);
60-
#else
61-
irq_enable(IRQ_LINE(ISR1_OFFSET));
62-
trigger_irq(IRQ_LINE(ISR1_OFFSET));
63-
#endif /* CONFIG_CPU_CORTEX_M */
64-
}
65-
#else
66-
void handler(void)
67-
{
68-
ztest_test_skip();
54+
printk("isr1: Enter\n");
55+
56+
/* Set verification token */
57+
isr1_result = ISR1_TOKEN;
58+
59+
printk("isr1: Leave\n");
6960
}
70-
#endif /* NO_TRIGGER_FROM_SW */
7161

7262
void isr0(void *param)
7363
{
7464
ARG_UNUSED(param);
75-
printk("%s running !!\n", __func__);
76-
#if defined(CONFIG_BOARD_QEMU_CORTEX_M0)
77-
/* QEMU Cortex-M0 timer emulation appears to not capturing the
78-
* current time accurately, resulting in erroneous busy wait
79-
* implementation.
80-
*
81-
* Work-around:
82-
* Increase busy-loop duration to ensure the timer interrupt will fire
83-
* during the busy loop waiting.
84-
*/
85-
k_busy_wait(MS_TO_US(1000));
86-
#else
87-
k_busy_wait(MS_TO_US(10));
88-
#endif
89-
printk("%s execution completed !!\n", __func__);
90-
zassert_equal(new_val, old_val, "Nested interrupt is not working\n");
65+
66+
printk("isr0: Enter\n");
67+
68+
/* Set verification token */
69+
isr0_result = ISR0_TOKEN;
70+
71+
/* Trigger nested IRQ 1 */
72+
trigger_irq(irq_line_1);
73+
74+
/* Wait for interrupt */
75+
k_busy_wait(MS_TO_US(DURATION));
76+
77+
/* Validate nested ISR result token */
78+
zassert_equal(isr1_result, ISR1_TOKEN, "isr1 did not execute");
79+
80+
printk("isr0: Leave\n");
9181
}
9282

9383
/**
84+
* @brief Test interrupt nesting
85+
*
86+
* @ingroup kernel_interrupt_tests
9487
*
95-
* Interrupt nesting feature allows an ISR to be preempted in mid-execution
96-
* if a higher priority interrupt is signaled. The lower priority ISR resumes
97-
* execution once the higher priority ISR has completed its processing.
98-
* The expected control flow should be isr0 -> handler -> isr1 -> isr0
88+
* This routine tests the interrupt nesting feature, which allows an ISR to be
89+
* preempted in mid-execution if a higher priority interrupt is signaled. The
90+
* lower priority ISR resumes execution once the higher priority ISR has
91+
* completed its processing.
92+
*
93+
* The expected control flow for this test is as follows:
94+
*
95+
* 1. [thread] Trigger IRQ 0 (lower priority)
96+
* 2. [isr0] Set ISR 0 result token and trigger IRQ 1 (higher priority)
97+
* 3. [isr1] Set ISR 1 result token and return
98+
* 4. [isr0] Validate ISR 1 result token and return
99+
* 5. [thread] Validate ISR 0 result token
99100
*/
100-
#ifndef NO_TRIGGER_FROM_SW
101101
void test_nested_isr(void)
102102
{
103+
/* Resolve test IRQ line numbers */
103104
#if defined(CONFIG_CPU_CORTEX_M)
104105
irq_line_0 = get_available_nvic_line(CONFIG_NUM_IRQS);
105106
irq_line_1 = get_available_nvic_line(irq_line_0);
106-
arch_irq_connect_dynamic(irq_line_0, ISR0_PRIO, isr0, NULL, 0);
107-
arch_irq_connect_dynamic(irq_line_1, ISR1_PRIO, isr1, NULL, 0);
108107
#else
109-
IRQ_CONNECT(IRQ_LINE(ISR0_OFFSET), ISR0_PRIO, isr0, NULL, 0);
110-
IRQ_CONNECT(IRQ_LINE(ISR1_OFFSET), ISR1_PRIO, isr1, NULL, 0);
111-
#endif /* CONFIG_CPU_CORTEX_M */
108+
irq_line_0 = IRQ0_LINE;
109+
irq_line_1 = IRQ1_LINE;
110+
#endif
112111

113-
k_timer_init(&timer, handler, NULL);
114-
k_timer_start(&timer, DURATION, K_NO_WAIT);
112+
/* Connect and enable test IRQs */
113+
arch_irq_connect_dynamic(irq_line_0, IRQ0_PRIO, isr0, NULL, 0);
114+
arch_irq_connect_dynamic(irq_line_1, IRQ1_PRIO, isr1, NULL, 0);
115115

116-
#if defined(CONFIG_CPU_CORTEX_M)
117116
irq_enable(irq_line_0);
117+
irq_enable(irq_line_1);
118+
119+
/* Trigger test IRQ 0 */
118120
trigger_irq(irq_line_0);
119-
#else
120-
irq_enable(IRQ_LINE(ISR0_OFFSET));
121-
trigger_irq(IRQ_LINE(ISR0_OFFSET));
122-
#endif /* CONFIG_CPU_CORTEX_M */
123121

122+
/* Wait for interrupt */
123+
k_busy_wait(MS_TO_US(DURATION));
124+
125+
/* Validate ISR result token */
126+
zassert_equal(isr0_result, ISR0_TOKEN, "isr0 did not execute");
124127
}
125128
#else
126129
void test_nested_isr(void)

0 commit comments

Comments
 (0)