ci(system): add test app for esp_intr_dump

pull/11821/head
Ivan Grokhotkov 2023-05-22 20:56:33 +02:00
rodzic 621afc48b1
commit 2bcb227542
Nie znaleziono w bazie danych klucza dla tego podpisu
ID klucza GPG: 1E050E141B280628
12 zmienionych plików z 563 dodań i 0 usunięć

Wyświetl plik

@ -0,0 +1,5 @@
cmake_minimum_required(VERSION 3.16)
set(COMPONENTS main)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(esp_intr_dump)

Wyświetl plik

@ -0,0 +1,14 @@
| Supported Targets | ESP32 | ESP32-C2 | ESP32-C3 | ESP32-C6 | ESP32-H2 | ESP32-S2 | ESP32-S3 |
| ----------------- | ----- | -------- | -------- | -------- | -------- | -------- | -------- |
# Test for esp_intr_dump
This test app serves two purposes:
1. Sanity-checking `esp_intr_dump` function. These tests run in QEMU and make sure that `esp_intr_dump` produces expected output when e.g. a shared interrupt is allocated.
2. Making unintended changes to the default interrupt allocations more visible in MRs. The way this works is, the output of `esp_intr_dump` is compared to the expected output, for example [expected_output/esp32.txt](expected_output/esp32.txt). If you change IDF startup code so that it allocates an additional interrupt, you will need to update the expected output file. MR reviewers will see the modification of the expected output file and will evaluate the impact of the change.
## When adding support for a new chip target
1. Build the test app for the new target, flash it to the board.
2. Enter `intr_dump` command in the console.
3. Copy the output and save it in `expected_output/<target>.txt`.

Wyświetl plik

@ -0,0 +1,70 @@
CPU 0 interrupt status:
Int Level Type Status
0 1 Level Reserved
1 1 Level Reserved
2 1 Level Used: RTC_CORE
3 1 Level Used: TG0_LACT_LEVEL
4 1 Level Reserved
5 1 Level Reserved
6 1 Level Reserved
7 1 Level CPU-internal
8 1 Level Reserved
9 1 Level Used: FROM_CPU0
10 1 Edge Free (not general-use)
11 3 Level CPU-internal
12 1 Level Used: TG0_WDT_LEVEL
13 1 Level Used: UART0
14 7 Level Reserved
15 3 Level CPU-internal
16 5 Level CPU-internal
17 1 Level Free
18 1 Level Free
19 2 Level Free
20 2 Level Free
21 2 Level Free
22 3 Edge Reserved
23 3 Level Free
24 4 Level Reserved
25 4 Level Reserved
26 5 Level Free (not general-use)
27 3 Level Reserved
28 4 Edge Free (not general-use)
29 3 Level CPU-internal
30 4 Edge Reserved
31 5 Level Reserved
CPU 1 interrupt status:
Int Level Type Status
0 1 Level Reserved
1 1 Level Reserved
2 1 Level Used: FROM_CPU1
3 1 Level Free
4 1 Level Free
5 1 Level Reserved
6 1 Level Reserved
7 1 Level CPU-internal
8 1 Level Reserved
9 1 Level Free
10 1 Edge Free (not general-use)
11 3 Level CPU-internal
12 1 Level Free
13 1 Level Free
14 7 Level Reserved
15 3 Level CPU-internal
16 5 Level CPU-internal
17 1 Level Free
18 1 Level Free
19 2 Level Free
20 2 Level Free
21 2 Level Free
22 3 Edge Free (not general-use)
23 3 Level Free
24 4 Level Free (not general-use)
25 4 Level Reserved
26 5 Level Reserved
27 3 Level Reserved
28 4 Edge Free (not general-use)
29 3 Level CPU-internal
30 4 Edge Reserved
31 5 Level Reserved
Interrupts available for general use: 17
Shared interrupts: 0

Wyświetl plik

@ -0,0 +1,36 @@
CPU 0 interrupt status:
Int Level Type Status
0 * * Reserved
1 * * Reserved
2 2 Level Used: RTC_CORE
3 2 Level Used: SYSTIMER_TARGET2_EDGE
4 2 Level Used: ETS_FROM_CPU_INTR0
5 * * Reserved
6 * * Reserved
7 2 Level Used: SYSTIMER_TARGET0_EDGE
8 * * Reserved
9 2 Level Used: UART
10 * * Free
11 * * Free
12 * * Free
13 * * Free
14 * * Free
15 * * Free
16 * * Free
17 * * Free
18 * * Free
19 * * Free
20 * * Free
21 * * Free
22 * * Free
23 * * Free
24 * * Reserved
25 * * Reserved
26 * * Free
27 * * Reserved
28 * * Free
29 * * Free
30 * * Free
31 * * Free
Interrupts available for general use: 19
Shared interrupts: 0

Wyświetl plik

@ -0,0 +1,36 @@
CPU 0 interrupt status:
Int Level Type Status
0 * * Reserved
1 * * Reserved
2 2 Level Used: RTC_CORE
3 2 Level Used: SYSTIMER_TARGET2_EDGE
4 2 Level Used: FROM_CPU_INTR0
5 * * Reserved
6 * * Reserved
7 2 Level Used: SYSTIMER_TARGET0_EDGE
8 * * Reserved
9 2 Level Used: TG0_WDT_LEVEL
10 2 Level Used: UART0
11 * * Free
12 * * Free
13 * * Free
14 * * Free
15 * * Free
16 * * Free
17 * * Free
18 * * Free
19 * * Free
20 * * Free
21 * * Free
22 * * Free
23 * * Free
24 * * Reserved
25 * * Reserved
26 * * Reserved
27 * * Reserved
28 * * Free
29 * * Free
30 * * Free
31 * * Free
Interrupts available for general use: 17
Shared interrupts: 0

Wyświetl plik

@ -0,0 +1,36 @@
CPU 0 interrupt status:
Int Level Type Status
0 * * Reserved
1 * * Reserved
2 2 Level Used: LP_RTC_TIMER
3 * * Reserved
4 * * Reserved
5 * * Reserved
6 * * Reserved
7 * * Reserved
8 * * Reserved
9 2 Level Used: SYSTIMER_TARGET2
10 2 Level Used: CPU_FROM_CPU_0
11 2 Level Used: SYSTIMER_TARGET0
12 2 Level Used: TG0_WDT
13 2 Level Used: UART0
14 * * Free
15 * * Free
16 * * Free
17 * * Free
18 * * Free
19 * * Free
20 * * Free
21 * * Free
22 * * Free
23 * * Free
24 * * Reserved
25 * * Reserved
26 * * Free
27 * * Reserved
28 * * Free
29 * * Free
30 * * Free
31 * * Free
Interrupts available for general use: 15
Shared interrupts: 0

Wyświetl plik

@ -0,0 +1,36 @@
CPU 0 interrupt status:
Int Level Type Status
0 * * Reserved
1 * * Reserved
2 2 Level Used: LP_RTC_TIMER
3 * * Reserved
4 * * Reserved
5 * * Reserved
6 * * Reserved
7 * * Reserved
8 * * Reserved
9 2 Level Used: SYSTIMER_TARGET2
10 2 Level Used: CPUFROM_CPU_0
11 2 Level Used: SYSTIMER_TARGET0
12 2 Level Used: TG0_WDT
13 2 Level Used: UART0
14 * * Free
15 * * Free
16 * * Free
17 * * Free
18 * * Free
19 * * Free
20 * * Free
21 * * Free
22 * * Free
23 * * Free
24 * * Reserved
25 * * Reserved
26 * * Free
27 * * Reserved
28 * * Free
29 * * Free
30 * * Free
31 * * Free
Interrupts available for general use: 15
Shared interrupts: 0

Wyświetl plik

@ -0,0 +1,36 @@
CPU 0 interrupt status:
Int Level Type Status
0 1 Level Reserved
1 1 Level Reserved
2 1 Level Used: RTC_CORE
3 1 Level Used: FROM_CPU_INTR0
4 1 Level Reserved
5 1 Level Reserved
6 1 Level Reserved
7 1 Level CPU-internal
8 1 Level Reserved
9 1 Level Used: TG0_WDT_LEVEL
10 1 Edge Used: SYSTIMER_TARGET2
11 3 Level CPU-internal
12 1 Level Used: UART0
13 1 Level Free
14 7 Level Reserved
15 3 Level CPU-internal
16 5 Level CPU-internal
17 1 Level Free
18 1 Level Free
19 2 Level Free
20 2 Level Free
21 2 Level Free
22 3 Edge Reserved
23 3 Level Free
24 4 Level Reserved
25 4 Level Reserved
26 5 Level Free (not general-use)
27 3 Level Reserved
28 4 Edge Free (not general-use)
29 3 Level CPU-internal
30 4 Edge Reserved
31 5 Level Reserved
Interrupts available for general use: 7
Shared interrupts: 0

Wyświetl plik

@ -0,0 +1,70 @@
CPU 0 interrupt status:
Int Level Type Status
0 1 Level Reserved
1 1 Level Reserved
2 1 Level Used: RTC_CORE
3 1 Level Used: SYSTIMER_TARGET2
4 1 Level Reserved
5 1 Level Reserved
6 1 Level CPU-internal
7 1 Level CPU-internal
8 1 Level Reserved
9 1 Level Used: FROM_CPU_INTR0
10 1 Edge Free (not general-use)
11 3 Level CPU-internal
12 1 Level Used: SYSTIMER_TARGET0
13 1 Level Used: TG0_WDT_LEVEL
14 7 Level Reserved
15 3 Level CPU-internal
16 5 Level CPU-internal
17 1 Level Used: UART0
18 1 Level Free
19 2 Level Free
20 2 Level Free
21 2 Level Free
22 3 Edge Reserved
23 3 Level Free
24 4 Level Reserved
25 4 Level Reserved
26 5 Level Free (not general-use)
27 3 Level Reserved
28 4 Edge Free (not general-use)
29 3 Level CPU-internal
30 4 Edge Reserved
31 5 Level Reserved
CPU 1 interrupt status:
Int Level Type Status
0 1 Level Reserved
1 1 Level Reserved
2 1 Level Used: FROM_CPU_INTR1
3 1 Level Used: SYSTIMER_TARGET1
4 1 Level Free
5 1 Level Reserved
6 1 Level CPU-internal
7 1 Level CPU-internal
8 1 Level Reserved
9 1 Level Free
10 1 Edge Free (not general-use)
11 3 Level CPU-internal
12 1 Level Free
13 1 Level Free
14 7 Level Reserved
15 3 Level CPU-internal
16 5 Level CPU-internal
17 1 Level Free
18 1 Level Free
19 2 Level Free
20 2 Level Free
21 2 Level Free
22 3 Edge Free (not general-use)
23 3 Level Free
24 4 Level Free (not general-use)
25 4 Level Reserved
26 5 Level Reserved
27 3 Level Reserved
28 4 Edge Free (not general-use)
29 3 Level CPU-internal
30 4 Edge Reserved
31 5 Level Reserved
Interrupts available for general use: 15
Shared interrupts: 0

Wyświetl plik

@ -0,0 +1,3 @@
idf_component_register(SRCS "test_esp_intr_dump_main.c"
PRIV_REQUIRES console
INCLUDE_DIRS ".")

Wyświetl plik

@ -0,0 +1,169 @@
/*
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <stdbool.h>
#include <string.h>
#include "esp_intr_alloc.h"
#include "esp_console.h"
#include "argtable3/argtable3.h"
#include "soc/interrupts.h"
static void start_console(void);
static void register_intr_commands(void);
void app_main(void)
{
start_console();
}
static void intr_handler_dummy(void* arg)
{
}
static void start_console(void)
{
esp_console_repl_t *repl = NULL;
esp_console_repl_config_t repl_config = ESP_CONSOLE_REPL_CONFIG_DEFAULT();
/* Prompt to be printed before each line.
* This can be customized, made dynamic, etc.
*/
repl_config.prompt = "test_intr_dump>";
repl_config.max_cmdline_length = 80;
/* Register commands */
esp_console_register_help_command();
register_intr_commands();
#if defined(CONFIG_ESP_CONSOLE_UART_DEFAULT) || defined(CONFIG_ESP_CONSOLE_UART_CUSTOM)
esp_console_dev_uart_config_t hw_config = ESP_CONSOLE_DEV_UART_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_console_new_repl_uart(&hw_config, &repl_config, &repl));
#elif defined(CONFIG_ESP_CONSOLE_USB_CDC)
esp_console_dev_usb_cdc_config_t hw_config = ESP_CONSOLE_DEV_CDC_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_console_new_repl_usb_cdc(&hw_config, &repl_config, &repl));
#elif defined(CONFIG_ESP_CONSOLE_USB_SERIAL_JTAG)
esp_console_dev_usb_serial_jtag_config_t hw_config = ESP_CONSOLE_DEV_USB_SERIAL_JTAG_CONFIG_DEFAULT();
ESP_ERROR_CHECK(esp_console_new_repl_usb_serial_jtag(&hw_config, &repl_config, &repl));
#else
#error Unsupported console type
#endif
ESP_ERROR_CHECK(esp_console_start_repl(repl));
}
static struct {
struct arg_str *source;
struct arg_str *flags;
struct arg_end *end;
} intr_alloc_args;
static int cmd_intr_alloc(int argc, char **argv)
{
int nerrors = arg_parse(argc, argv, (void **) &intr_alloc_args);
if (nerrors != 0) {
arg_print_errors(stderr, intr_alloc_args.end, argv[0]);
return 1;
}
assert(intr_alloc_args.source->count == 1);
assert(intr_alloc_args.flags->count == 1);
const char* source_str = intr_alloc_args.source->sval[0];
const char* flags_str = intr_alloc_args.flags->sval[0];
// find the interrupt source number
int source_num = -1;
for (int i = 0; i < ETS_MAX_INTR_SOURCE; i++) {
if (strcmp(source_str, esp_isr_names[i]) == 0) {
source_num = i;
break;
}
}
if (source_num == -1) {
printf("Unknown interrupt source: %s. Please check components/soc/" CONFIG_IDF_TARGET "/interrupts.c for the list\n", source_str);
return 1;
}
// parse flags
typedef struct {
const char* name;
int mask;
} flag_desc_t;
const flag_desc_t flag_desc[] = {
{ .name = "LEVEL1", .mask = ESP_INTR_FLAG_LEVEL1 },
{ .name = "LEVEL2", .mask = ESP_INTR_FLAG_LEVEL2 },
{ .name = "LEVEL3", .mask = ESP_INTR_FLAG_LEVEL3 },
{ .name = "LEVEL4", .mask = ESP_INTR_FLAG_LEVEL4 },
{ .name = "LEVEL5", .mask = ESP_INTR_FLAG_LEVEL5 },
{ .name = "LEVEL6", .mask = ESP_INTR_FLAG_LEVEL6 },
{ .name = "NMI", .mask = ESP_INTR_FLAG_NMI },
{ .name = "SHARED", .mask = ESP_INTR_FLAG_SHARED },
{ .name = "EDGE", .mask = ESP_INTR_FLAG_EDGE },
{ .name = "IRAM", .mask = ESP_INTR_FLAG_IRAM },
{ .name = "LOWMED", .mask = ESP_INTR_FLAG_LOWMED },
{ .name = "HIGH", .mask = ESP_INTR_FLAG_HIGH },
};
// split flags_str string into tokens separated by | character and get the final bitmask
int flags = 0;
char* flags_str_copy = strdup(flags_str);
char* token = strtok(flags_str_copy, "|");
while (token != NULL) {
bool found = false;
for (int i = 0; i < sizeof(flag_desc)/sizeof(flag_desc[0]); i++) {
if (strcmp(token, flag_desc[i].name) == 0) {
flags |= flag_desc[i].mask;
found = true;
break;
}
}
if (!found) {
printf("Unknown flag: %s\n", token);
free(flags_str_copy);
return 1;
}
token = strtok(NULL, "|");
}
free(flags_str_copy);
// allocate the interrupt
esp_err_t ret = esp_intr_alloc(source_num, flags, intr_handler_dummy, NULL, NULL);
if (ret != ESP_OK) {
printf("Failed to allocate interrupt (source: %d, flags: 0x%x): %s\n", source_num, flags, esp_err_to_name(ret));
return 1;
}
return 0;
}
int cmd_intr_dump(int argc, char **argv)
{
esp_intr_dump(stdout);
return 0;
}
static void register_intr_commands(void)
{
intr_alloc_args.source = arg_str1(NULL, NULL, "<source>", "Interrupt source name");
intr_alloc_args.flags = arg_str1(NULL, NULL, "<flags>", "Interrupt flags separated by | character: LEVEL1|LEVEL2|...|LEVEL6|NMI|SHARED|EDGE|IRAM|LOWMED|HIGH");
intr_alloc_args.end = arg_end(2);
const esp_console_cmd_t cmd_intr_alloc_info = {
.command = "intr_alloc",
.help = "Allocate an interrupt",
.hint = NULL,
.func = &cmd_intr_alloc,
.argtable = &intr_alloc_args
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_intr_alloc_info));
const esp_console_cmd_t cmd_intr_dump_info = {
.command = "intr_dump",
.help = "Dump interrupt information",
.hint = NULL,
.func = &cmd_intr_dump,
};
ESP_ERROR_CHECK(esp_console_cmd_register(&cmd_intr_dump_info));
}

Wyświetl plik

@ -0,0 +1,52 @@
# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
# SPDX-License-Identifier: CC0-1.0
import os
import pytest
from pytest_embedded import Dut
PROMPT = 'test_intr_dump>'
@pytest.mark.esp32
@pytest.mark.qemu
def test_esp_intr_dump_nonshared(dut: Dut) -> None:
dut.expect_exact(PROMPT, timeout=10)
dut.write('intr_alloc GPIO LEVEL3\n')
dut.expect_exact(PROMPT)
dut.write('intr_dump\n')
dut.expect(r'(\d+)\s+3\s+Level\s+Used: GPIO')
@pytest.mark.esp32
@pytest.mark.qemu
def test_esp_intr_dump_shared(dut: Dut) -> None:
dut.expect_exact(PROMPT, timeout=10)
dut.write('intr_alloc GPIO SHARED\n')
dut.expect_exact(PROMPT)
dut.write('intr_dump\n')
dut.expect(r'(\d+)\s+1\s+Level\s+Shared: GPIO')
dut.expect_exact(PROMPT)
dut.write('intr_alloc UART1 SHARED\n')
dut.expect_exact(PROMPT)
dut.write('intr_dump\n')
dut.expect(r'(\d+)\s+1\s+Level\s+Shared: UART1 GPIO')
dut.expect_exact('Shared interrupts: 1')
dut.expect_exact(PROMPT)
@pytest.mark.supported_targets
@pytest.mark.generic
def test_esp_intr_dump_expected_output(dut: Dut) -> None:
dut.expect_exact(PROMPT, timeout=10)
dut.write('intr_dump\n')
exp_out_file = os.path.join(os.path.dirname(__file__), 'expected_output', f'{dut.target}.txt')
for line in open(exp_out_file, 'r').readlines():
dut.expect_exact(line.strip())