diff --git a/tools/test_apps/system/esp_intr_dump/CMakeLists.txt b/tools/test_apps/system/esp_intr_dump/CMakeLists.txt new file mode 100644 index 0000000000..3d658bd5e6 --- /dev/null +++ b/tools/test_apps/system/esp_intr_dump/CMakeLists.txt @@ -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) diff --git a/tools/test_apps/system/esp_intr_dump/README.md b/tools/test_apps/system/esp_intr_dump/README.md new file mode 100644 index 0000000000..65ecf215e1 --- /dev/null +++ b/tools/test_apps/system/esp_intr_dump/README.md @@ -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/.txt`. diff --git a/tools/test_apps/system/esp_intr_dump/expected_output/esp32.txt b/tools/test_apps/system/esp_intr_dump/expected_output/esp32.txt new file mode 100644 index 0000000000..1533cf38ad --- /dev/null +++ b/tools/test_apps/system/esp_intr_dump/expected_output/esp32.txt @@ -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 diff --git a/tools/test_apps/system/esp_intr_dump/expected_output/esp32c2.txt b/tools/test_apps/system/esp_intr_dump/expected_output/esp32c2.txt new file mode 100644 index 0000000000..7f0ed34b7f --- /dev/null +++ b/tools/test_apps/system/esp_intr_dump/expected_output/esp32c2.txt @@ -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 diff --git a/tools/test_apps/system/esp_intr_dump/expected_output/esp32c3.txt b/tools/test_apps/system/esp_intr_dump/expected_output/esp32c3.txt new file mode 100644 index 0000000000..cf0079d4d4 --- /dev/null +++ b/tools/test_apps/system/esp_intr_dump/expected_output/esp32c3.txt @@ -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 diff --git a/tools/test_apps/system/esp_intr_dump/expected_output/esp32c6.txt b/tools/test_apps/system/esp_intr_dump/expected_output/esp32c6.txt new file mode 100644 index 0000000000..eaf1d5775b --- /dev/null +++ b/tools/test_apps/system/esp_intr_dump/expected_output/esp32c6.txt @@ -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 diff --git a/tools/test_apps/system/esp_intr_dump/expected_output/esp32h2.txt b/tools/test_apps/system/esp_intr_dump/expected_output/esp32h2.txt new file mode 100644 index 0000000000..a6b02f59f9 --- /dev/null +++ b/tools/test_apps/system/esp_intr_dump/expected_output/esp32h2.txt @@ -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 diff --git a/tools/test_apps/system/esp_intr_dump/expected_output/esp32s2.txt b/tools/test_apps/system/esp_intr_dump/expected_output/esp32s2.txt new file mode 100644 index 0000000000..50b75a349c --- /dev/null +++ b/tools/test_apps/system/esp_intr_dump/expected_output/esp32s2.txt @@ -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 diff --git a/tools/test_apps/system/esp_intr_dump/expected_output/esp32s3.txt b/tools/test_apps/system/esp_intr_dump/expected_output/esp32s3.txt new file mode 100644 index 0000000000..fb3ee32402 --- /dev/null +++ b/tools/test_apps/system/esp_intr_dump/expected_output/esp32s3.txt @@ -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 diff --git a/tools/test_apps/system/esp_intr_dump/main/CMakeLists.txt b/tools/test_apps/system/esp_intr_dump/main/CMakeLists.txt new file mode 100644 index 0000000000..4cf9025ed8 --- /dev/null +++ b/tools/test_apps/system/esp_intr_dump/main/CMakeLists.txt @@ -0,0 +1,3 @@ +idf_component_register(SRCS "test_esp_intr_dump_main.c" + PRIV_REQUIRES console + INCLUDE_DIRS ".") diff --git a/tools/test_apps/system/esp_intr_dump/main/test_esp_intr_dump_main.c b/tools/test_apps/system/esp_intr_dump/main/test_esp_intr_dump_main.c new file mode 100644 index 0000000000..6c96c0312b --- /dev/null +++ b/tools/test_apps/system/esp_intr_dump/main/test_esp_intr_dump_main.c @@ -0,0 +1,169 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include +#include +#include +#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, "", "Interrupt source name"); + intr_alloc_args.flags = arg_str1(NULL, NULL, "", "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)); +} diff --git a/tools/test_apps/system/esp_intr_dump/pytest_esp_intr_dump.py b/tools/test_apps/system/esp_intr_dump/pytest_esp_intr_dump.py new file mode 100644 index 0000000000..161b6ef7d0 --- /dev/null +++ b/tools/test_apps/system/esp_intr_dump/pytest_esp_intr_dump.py @@ -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())