Skip to content

Commit ccf27a9

Browse files
Marek Matejcfriedt
authored andcommitted
tests: boards: espressif: Nested IRQ
Add test suite to test nested interrupts on ESP32 Xtensa targets. Signed-off-by: Marek Matej <[email protected]>
1 parent 9cc26d6 commit ccf27a9

File tree

8 files changed

+341
-0
lines changed

8 files changed

+341
-0
lines changed
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# SPDX-License-Identifier: Apache-2.0
2+
3+
cmake_minimum_required(VERSION 3.20.0)
4+
5+
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
6+
project(esp_interrupt_test)
7+
8+
target_sources(app PRIVATE src/main.c)
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
.. _interrupt_test:
2+
3+
Espressif's Xtensa interrupt test
4+
#################################
5+
6+
Overview
7+
********
8+
9+
Test objective is to verify that the ESP32, ESP32-S2 and ESP32-S3 platforms correctly support nested interrupts, where higher-priority interrupts can preempt lower-priority ones.
10+
11+
Three independent hardware timers are configured.
12+
13+
Each timer is assigned an ISR (Interrupt Service Routine) with a priority one level higher than the previous timer.
14+
15+
* Timer 1: lowest priority ISR
16+
* Timer 2: medium priority ISR
17+
* Timer 3: highest priority ISR
18+
19+
Execution Flow
20+
--------------
21+
22+
* Timer 1 ISR triggers first. - It simulates work (waits briefly).
23+
* Before completing, it triggers Timer 2 interrupt.
24+
* Timer 2 ISR preempts Timer 1 ISR (because of higher priority). - It also simulates work.
25+
* It triggers Timer 3 interrupt.
26+
* Timer 3 ISR preempts Timer 2 ISR (highest priority). - It performs its work and completes execution.
27+
* After Timer 3 ISR completes, control returns to Timer 2 ISR, which then completes.
28+
* Finally, execution resumes and completes Timer 1 ISR.
29+
30+
Success Criteria
31+
----------------
32+
33+
Each ISR execution is verified by setting a unique "magic value". The test is successful if:
34+
35+
* Timer 1 ISR sets its magic value.
36+
* Timer 2 ISR sets its magic value.
37+
* Timer 3 ISR sets its magic value.
38+
39+
This confirms that ISR priorities are respected and preemption works correctly.
40+
41+
Supported Boards
42+
****************
43+
44+
- esp32_devkitc/esp32/procpu
45+
- esp32s2_saola
46+
- esp32s3_devkitm/esp32s3/procpu
47+
48+
Building and Running
49+
********************
50+
51+
Make sure you have the target connected over USB port.
52+
53+
.. code-block:: console
54+
55+
west build -b <board> tests/boards/espressif/interrupt
56+
west flash && west espressif monitor
57+
58+
To run the test with twister, use the following command:
59+
60+
.. code-block:: console
61+
62+
west twister -p <board> --device-testing --device-serial=/dev/ttyUSB0 -vv --flash-before -T tests/boards/espressif/interrupt
63+
64+
If the external 32K crystal is connect to pins 32K_XP and 32K_XN, the test can be run with ``external_xtal`` fixture enabled:
65+
66+
.. code-block:: console
67+
68+
west twister -p esp32s3_devkitm --device-testing --device-serial=/dev/ttyUSB0 -vv --flash-before -T tests/boards/espressif/interrupt
69+
70+
Sample Output
71+
=============
72+
73+
.. code-block:: console
74+
75+
*** Booting Zephyr OS build v4.2.0-2885-g158729997aec ***
76+
Running TESTSUITE esp_interrupt
77+
===================================================================
78+
START - test_nested_isr
79+
PASS - test_nested_isr in 10.101 seconds
80+
===================================================================
81+
TESTSUITE esp_interrupt succeeded
82+
------ TESTSUITE SUMMARY START ------
83+
SUITE PASS - 100.00% [esp_interrupt]: pass = 1, fail = 0, skip = 0, total = 1 duration = 10.101 seconds
84+
- PASS - [esp_interrupt.test_nested_isr] duration = 10.101 seconds
85+
------ TESTSUITE SUMMARY END ------
86+
===================================================================
87+
PROJECT EXECUTION SUCCESSFUL
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
CONFIG_ZTEST=y
2+
CONFIG_COUNTER=y
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (c) 2025 Espressif Systems (Shanghai) Co., Ltd.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
&timer0 {
8+
status = "okay";
9+
interrupts = <TG0_T0_LEVEL_INTR_SOURCE 1 0>;
10+
11+
counter {
12+
status = "okay";
13+
};
14+
};
15+
16+
&timer1 {
17+
status = "okay";
18+
interrupts = <TG0_T1_LEVEL_INTR_SOURCE 2 0>;
19+
20+
counter {
21+
status = "okay";
22+
};
23+
};
24+
25+
&timer2 {
26+
status = "okay";
27+
interrupts = <TG1_T0_LEVEL_INTR_SOURCE 3 0>;
28+
29+
counter {
30+
status = "okay";
31+
};
32+
};
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (c) 2025 Espressif Systems (Shanghai) Co., Ltd.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
&timer0 {
8+
status = "okay";
9+
interrupts = <TG0_T0_LEVEL_INTR_SOURCE 1 0>;
10+
11+
counter {
12+
status = "okay";
13+
};
14+
};
15+
16+
&timer1 {
17+
status = "okay";
18+
interrupts = <TG0_T1_LEVEL_INTR_SOURCE 2 0>;
19+
20+
counter {
21+
status = "okay";
22+
};
23+
};
24+
25+
&timer2 {
26+
status = "okay";
27+
interrupts = <TG1_T0_LEVEL_INTR_SOURCE 3 0>;
28+
29+
counter {
30+
status = "okay";
31+
};
32+
};
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (c) 2025 Espressif Systems (Shanghai) Co., Ltd.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
&timer0 {
8+
status = "okay";
9+
interrupts = <TG0_T0_LEVEL_INTR_SOURCE 1 0>;
10+
11+
counter {
12+
status = "okay";
13+
};
14+
};
15+
16+
&timer1 {
17+
status = "okay";
18+
interrupts = <TG0_T1_LEVEL_INTR_SOURCE 2 0>;
19+
20+
counter {
21+
status = "okay";
22+
};
23+
};
24+
25+
&timer2 {
26+
status = "okay";
27+
interrupts = <TG1_T0_LEVEL_INTR_SOURCE 3 0>;
28+
29+
counter {
30+
status = "okay";
31+
};
32+
};
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
/*
2+
* Copyright (c) 2025 Espressif Systems (Shanghai) Co., Ltd.
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
#include <zephyr/kernel.h>
8+
#include <zephyr/ztest.h>
9+
#include <zephyr/device.h>
10+
#include <zephyr/devicetree.h>
11+
#include <zephyr/drivers/counter.h>
12+
#include <zephyr/sys/printk.h>
13+
14+
/* Test sets the three timer units, each with different interrupt priority level.
15+
* Each timer/counter is set to respond to three separate alarms.
16+
* The alarm times are set so each would trigger one after another separated by
17+
* the `ALARM_DIFF_US` time.
18+
* Each ISR sets its corresponding token into its token slots and wait for the
19+
* subsequent alarm ISR to be called.
20+
* After the last alarm ISR finishes the main loop checks if the ISR were called.
21+
*/
22+
23+
#define TOKEN_A 0xBEEFBABE
24+
#define TOKEN_B 0xC0FEB00B
25+
#define TOKEN_C 0xDEAD5AA5
26+
27+
#define ALARM_DIFF_US 1000
28+
#define ALARM_A_US (1000 + ALARM_DIFF_US)
29+
#define ALARM_B_US (ALARM_A_US + ALARM_DIFF_US)
30+
#define ALARM_C_US (ALARM_B_US + ALARM_DIFF_US)
31+
#define ISR_DELAY_US (ALARM_DIFF_US * 3)
32+
#define CYCLE_MS 10
33+
#define TEST_CYCLES 1000
34+
35+
static const struct device *const timer0 = DEVICE_DT_GET(DT_INST(0, espressif_esp32_counter));
36+
static const struct device *const timer1 = DEVICE_DT_GET(DT_INST(1, espressif_esp32_counter));
37+
static const struct device *const timer2 = DEVICE_DT_GET(DT_INST(2, espressif_esp32_counter));
38+
39+
static struct counter_alarm_cfg alarm_a;
40+
static struct counter_alarm_cfg alarm_b;
41+
static struct counter_alarm_cfg alarm_c;
42+
43+
static int token[3];
44+
static int success_cnt;
45+
static int error_cnt;
46+
47+
static void alarm_handler_c(const struct device *dev, uint8_t chan_id, uint32_t counter,
48+
void *user_data)
49+
{
50+
if (dev == timer2 && token[0] == TOKEN_A && token[1] == TOKEN_B && token[2] == 0) {
51+
token[2] = TOKEN_C;
52+
}
53+
54+
k_busy_wait(ISR_DELAY_US);
55+
}
56+
57+
static void alarm_handler_b(const struct device *dev, uint8_t chan_id, uint32_t counter,
58+
void *user_data)
59+
{
60+
if (dev == timer1 && token[0] == TOKEN_A && token[1] == 0 && token[2] == 0) {
61+
token[1] = TOKEN_B;
62+
}
63+
64+
k_busy_wait(ISR_DELAY_US);
65+
}
66+
67+
static void alarm_handler_a(const struct device *dev, uint8_t chan_id, uint32_t counter,
68+
void *user_data)
69+
{
70+
if (dev == timer0 && token[0] == 0 && token[1] == 0 && token[2] == 0) {
71+
token[0] = TOKEN_A;
72+
}
73+
74+
k_busy_wait(ISR_DELAY_US);
75+
}
76+
77+
static void *esp_interrupt_suite_setup(void)
78+
{
79+
zassert_true(device_is_ready(timer0), "Device %s not ready", timer0->name);
80+
zassert_true(device_is_ready(timer1), "Device %s not ready", timer1->name);
81+
zassert_true(device_is_ready(timer2), "Device %s not ready", timer2->name);
82+
83+
zassert_false(counter_start(timer0), "Timer 0 failed to start");
84+
zassert_false(counter_start(timer1), "Timer 1 failed to start");
85+
zassert_false(counter_start(timer2), "Timer 2 failed to start");
86+
87+
return NULL;
88+
}
89+
90+
91+
ZTEST(esp_interrupt, test_nested_isr)
92+
{
93+
uint32_t cnt = TEST_CYCLES;
94+
95+
success_cnt = 0;
96+
error_cnt = 0;
97+
98+
while (cnt--) {
99+
alarm_a.ticks = counter_us_to_ticks(timer0, ALARM_A_US);
100+
alarm_a.callback = alarm_handler_a;
101+
102+
alarm_b.ticks = counter_us_to_ticks(timer1, ALARM_B_US);
103+
alarm_b.callback = alarm_handler_b;
104+
105+
alarm_c.ticks = counter_us_to_ticks(timer2, ALARM_C_US);
106+
alarm_c.callback = alarm_handler_c;
107+
108+
counter_reset(timer0);
109+
counter_reset(timer1);
110+
counter_reset(timer2);
111+
112+
zassert_false(counter_set_channel_alarm(timer0, 0, &alarm_a),
113+
"Failed to set alarm A");
114+
zassert_false(counter_set_channel_alarm(timer1, 0, &alarm_b),
115+
"Failed to set alarm B");
116+
zassert_false(counter_set_channel_alarm(timer2, 0, &alarm_c),
117+
"Failed to set alarm C");
118+
119+
k_msleep(CYCLE_MS);
120+
121+
if (token[0] == TOKEN_A && token[1] == TOKEN_B && token[2] == TOKEN_C) {
122+
success_cnt++;
123+
} else {
124+
error_cnt++;
125+
}
126+
127+
token[0] = 0;
128+
token[1] = 0;
129+
token[2] = 0;
130+
}
131+
132+
zassert_true(error_cnt == 0, "Errors occurred (%d)", error_cnt);
133+
zassert_true(success_cnt == TEST_CYCLES, "Not all test cycles pssed (%d from %d)",
134+
success_cnt, TEST_CYCLES);
135+
}
136+
137+
ZTEST_SUITE(esp_interrupt, NULL, esp_interrupt_suite_setup, NULL, NULL, NULL);
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
common:
2+
integration_platforms:
3+
- esp32s3_devkitm/esp32s3/procpu
4+
tests:
5+
boards.esp32.interrupts:
6+
tags:
7+
- drivers
8+
platform_allow:
9+
- esp32_devkitc/esp32/procpu
10+
- esp32s2_saola
11+
- esp32s3_devkitm/esp32s3/procpu

0 commit comments

Comments
 (0)