mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
Merge branch 'feature/esp_intr_dump' into 'master'
system: add esp_intr_dump API to debug interrupt allocation Closes IDF-4281 and IDF-6066 See merge request espressif/esp-idf!23877
This commit is contained in:
commit
e6c3f62281
@ -8,6 +8,7 @@
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include "esp_err.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
@ -307,6 +308,13 @@ static inline int esp_intr_flags_to_level(int flags)
|
||||
return __builtin_ffs((flags & ESP_INTR_FLAG_LEVELMASK) >> 1) + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Dump the status of allocated interrupts
|
||||
* @param stream The stream to dump to, if NULL then stdout is used
|
||||
* @return ESP_OK on success
|
||||
*/
|
||||
esp_err_t esp_intr_dump(FILE *stream);
|
||||
|
||||
/**@}*/
|
||||
|
||||
|
||||
|
@ -22,6 +22,9 @@
|
||||
#include "esp_attr.h"
|
||||
#include "esp_cpu.h"
|
||||
#include "esp_private/rtc_ctrl.h"
|
||||
#include "soc/interrupts.h"
|
||||
#include "soc/soc_caps.h"
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if !CONFIG_FREERTOS_UNICORE
|
||||
#include "esp_ipc.h"
|
||||
@ -539,6 +542,7 @@ esp_err_t esp_intr_alloc_intrstatus(int source, int flags, uint32_t intrstatusre
|
||||
//None found. Bail out.
|
||||
portEXIT_CRITICAL(&spinlock);
|
||||
free(ret);
|
||||
ESP_LOGE(TAG, "No free interrupt inputs for %s interrupt (flags 0x%X)", esp_isr_names[source], flags);
|
||||
return ESP_ERR_NOT_FOUND;
|
||||
}
|
||||
//Get an int vector desc for int.
|
||||
@ -902,3 +906,84 @@ void IRAM_ATTR esp_intr_disable_source(int inum)
|
||||
{
|
||||
esp_cpu_intr_disable(1 << inum);
|
||||
}
|
||||
|
||||
esp_err_t esp_intr_dump(FILE *stream)
|
||||
{
|
||||
if (stream == NULL) {
|
||||
stream = stdout;
|
||||
}
|
||||
#ifdef CONFIG_ESP_SYSTEM_SINGLE_CORE_MODE
|
||||
const int cpu_num = 1;
|
||||
#else
|
||||
const int cpu_num = SOC_CPU_CORES_NUM;
|
||||
#endif
|
||||
|
||||
int general_use_ints_free = 0;
|
||||
int shared_ints = 0;
|
||||
|
||||
for (int cpu = 0; cpu < cpu_num; ++cpu) {
|
||||
fprintf(stream, "CPU %d interrupt status:\n", cpu);
|
||||
fprintf(stream, " Int Level Type Status\n");
|
||||
for (int i_num = 0; i_num < 32; ++i_num) {
|
||||
fprintf(stream, " %2d ", i_num);
|
||||
esp_cpu_intr_desc_t intr_desc;
|
||||
esp_cpu_intr_get_desc(cpu, i_num, &intr_desc);
|
||||
bool is_general_use = true;
|
||||
vector_desc_t *vd = find_desc_for_int(i_num, cpu);
|
||||
|
||||
#ifndef SOC_CPU_HAS_FLEXIBLE_INTC
|
||||
fprintf(stream, " %d %s ",
|
||||
intr_desc.priority,
|
||||
intr_desc.type == ESP_CPU_INTR_TYPE_EDGE ? "Edge " : "Level");
|
||||
|
||||
is_general_use = (intr_desc.type == ESP_CPU_INTR_TYPE_LEVEL) && (intr_desc.priority <= XCHAL_EXCM_LEVEL);
|
||||
#else // SOC_CPU_HAS_FLEXIBLE_INTC
|
||||
if (vd == NULL) {
|
||||
fprintf(stream, " * * ");
|
||||
} else {
|
||||
// esp_cpu_intr_get_* functions need to be extended with cpu parameter.
|
||||
// Showing info for the current cpu only, in the meantime.
|
||||
if (esp_cpu_get_core_id() == cpu) {
|
||||
fprintf(stream, " %d %s ",
|
||||
esp_cpu_intr_get_priority(i_num),
|
||||
esp_cpu_intr_get_type(i_num) == ESP_CPU_INTR_TYPE_EDGE ? "Edge " : "Level");
|
||||
} else {
|
||||
fprintf(stream, " ? ? ");
|
||||
}
|
||||
}
|
||||
#endif // SOC_CPU_HAS_FLEXIBLE_INTC
|
||||
|
||||
if (intr_desc.flags & ESP_CPU_INTR_DESC_FLAG_RESVD) {
|
||||
fprintf(stream, "Reserved");
|
||||
} else if (intr_desc.flags & ESP_CPU_INTR_DESC_FLAG_SPECIAL) {
|
||||
fprintf(stream, "CPU-internal");
|
||||
} else {
|
||||
if (vd == NULL || (vd->flags & (VECDESC_FL_RESERVED | VECDESC_FL_NONSHARED | VECDESC_FL_SHARED)) == 0) {
|
||||
fprintf(stream, "Free");
|
||||
if (is_general_use) {
|
||||
++general_use_ints_free;
|
||||
} else {
|
||||
fprintf(stream, " (not general-use)");
|
||||
}
|
||||
} else if (vd->flags & VECDESC_FL_RESERVED) {
|
||||
fprintf(stream, "Reserved (run-time)");
|
||||
} else if (vd->flags & VECDESC_FL_NONSHARED) {
|
||||
fprintf(stream, "Used: %s", esp_isr_names[vd->source]);
|
||||
} else if (vd->flags & VECDESC_FL_SHARED) {
|
||||
fprintf(stream, "Shared: ");
|
||||
for (shared_vector_desc_t *svd = vd->shared_vec_info; svd != NULL; svd = svd->next) {
|
||||
fprintf(stream, "%s ", esp_isr_names[svd->source]);
|
||||
}
|
||||
++shared_ints;
|
||||
} else {
|
||||
fprintf(stream, "Unknown, flags = 0x%x", vd->flags);
|
||||
}
|
||||
}
|
||||
|
||||
fprintf(stream, "\n");
|
||||
}
|
||||
}
|
||||
fprintf(stream, "Interrupts available for general use: %d\n", general_use_ints_free);
|
||||
fprintf(stream, "Shared interrupts: %d\n", shared_ints);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
@ -85,6 +85,8 @@ It can also be useful to keep an interrupt handler in IRAM if it is called very
|
||||
|
||||
Refer to the :ref:`SPI flash API documentation <iram-safe-interrupt-handlers>` for more details.
|
||||
|
||||
.. _intr-alloc-shared-interrupts:
|
||||
|
||||
Multiple Handlers Sharing A Source
|
||||
----------------------------------
|
||||
|
||||
@ -92,10 +94,55 @@ Several handlers can be assigned to a same source, given that all handlers are a
|
||||
|
||||
Sources attached to non-shared interrupt do not support this feature.
|
||||
|
||||
Though the framework support this feature, you have to use it *very carefully*. There usually exist two ways to stop an interrupt from being triggered: *disable the source* or *mask peripheral interrupt status*. IDF only handles enabling and disabling of the source itself, leaving status and mask bits to be handled by users.
|
||||
.. only:: not SOC_CPU_HAS_FLEXIBLE_INTC
|
||||
|
||||
By default, when ``ESP_INTR_FLAG_SHARED`` flag is specified, the interrupt allocator will allocate only Level 1 interrupts. Use ``ESP_INTR_FLAG_SHARED | ESP_INTR_FLAG_LOWMED`` to also allow allocating shared interrupts at Level 2 and Level 3.
|
||||
|
||||
Though the framework supports this feature, you have to use it *very carefully*. There usually exist two ways to stop an interrupt from being triggered: *disable the source* or *mask peripheral interrupt status*. IDF only handles enabling and disabling of the source itself, leaving status and mask bits to be handled by users.
|
||||
**Status bits shall either be masked before the handler responsible for it is disabled, either be masked and then properly handled in another enabled interrupt**.
|
||||
Please note that leaving some status bits unhandled without masking them, while disabling the handlers for them, will cause the interrupt(s) to be triggered indefinitely, resulting therefore in a system crash.
|
||||
|
||||
Troubleshooting Interrupt Allocation
|
||||
------------------------------------
|
||||
|
||||
On most Espressif SoCs CPU interrupts are a limited resource. Therefore it is possible to run a program which runs out of CPU interrupts, for example by initializing several peripheral drivers. Typically this will result in the driver initialization function returning ``ESP_ERR_NOT_FOUND`` error code.
|
||||
|
||||
If this happens, you can use :cpp:func:`esp_intr_dump` function to print the list of interrupts along with their status. The output of this function typically looks like this::
|
||||
|
||||
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
|
||||
...
|
||||
|
||||
The columns of the output have the following meaning:
|
||||
|
||||
.. list::
|
||||
|
||||
- ``Int``: CPU interrupt input number. This is typically not used in software directly, and is provided for reference only.
|
||||
:not SOC_CPU_HAS_FLEXIBLE_INTC: - ``Level``: Interrupt level (1-7) of the CPU interrupt. This level is fixed in hardware, and cannot be changed.
|
||||
:SOC_CPU_HAS_FLEXIBLE_INTC: - ``Level``: For interrupts which have been allocated, the level (priority) of the interrupt. For free interrupts ``*`` is printed.
|
||||
:not SOC_CPU_HAS_FLEXIBLE_INTC: - ``Type``: Interrupt type (Level or Edge) of the CPU interrupt. This type is fixed in hardware, and cannot be changed.
|
||||
:SOC_CPU_HAS_FLEXIBLE_INTC: - ``Type``: For interrupts which have been allocated, the type (Level or Edge) of the interrupt. For free interrupts ``*`` is printed.
|
||||
- ``Status``: One of the possible statuses of the interrupt:
|
||||
- ``Reserved``: The interrupt is reserved either at hardware level, or by one of the parts of ESP-IDF. It can not be allocated using :cpp:func:`esp_intr_alloc`.
|
||||
- ``Used: <source>``: The interrupt is allocated and connected to a single peripheral.
|
||||
- ``Shared: <source1> <source2> ...``: The interrupt is allocated and connected to multiple peripherals. See :ref:`intr-alloc-shared-interrupts` above.
|
||||
- ``Free``: The interrupt is not allocated and can be used by :cpp:func:`esp_intr_alloc`.
|
||||
:not SOC_CPU_HAS_FLEXIBLE_INTC: - ``Free (not general-use)``: The interrupt is not allocated, but is either a high-level interrupt (level 4-7) or and edge-triggered interrupt. High-level interrupts can be allocated using :cpp:func:`esp_intr_alloc` but require the handlers to be written in Assembly, see :doc:`../../api-guides/hlinterrupts`. Edge-triggered low- and medium- level interrupts can also be allocated using :cpp:func:`esp_intr_alloc`, but are not used often since most peripheral interrupts are level-triggered.
|
||||
|
||||
If you have confirmed that the application is indeed running out of interrupts, a combination of the following suggestions can help resolve the issue:
|
||||
|
||||
.. list::
|
||||
|
||||
:not CONFIG_FREERTOS_UNICORE: - On multi-core SoCs, try initializing some of the peripheral drivers from a task pinned to the second core. Interrupts are typically allocated on the same core where the peripheral driver initialization function runs. Therefore by running the initialization function on the second core, more interrupt inputs can be used.
|
||||
- Determine the interrupts which can tolerate higher latency, and allocate them using ``ESP_INTR_FLAG_SHARED`` flag (optionally ORed with ``ESP_INTR_FLAG_LOWMED``). Using this flag for two or more peripherals will let them use a single interrupt input, and therefore save interrupt inputs for other peripherals. See :ref:`intr-alloc-shared-interrupts` above.
|
||||
:not SOC_CPU_HAS_FLEXIBLE_INTC: - Some peripheral driver may default to allocating interrupts with ``ESP_INTR_FLAG_LEVEL1`` flag, so level 2 and 3 interrupts won't get used by default. If :cpp:func:`esp_intr_dump` shows that some level 2 or 3 interrupts are available, try changing the interrupt allocation flags when initializing the driver to ``ESP_INTR_FLAG_LEVEL2`` or ``ESP_INTR_FLAG_LEVEL3``.
|
||||
- Check if some of the peripheral drivers do not need to be used all the time, and initialize/deinitialize them on demand. This can reduce the number of simultaneously allocated interrupts.
|
||||
|
||||
|
||||
API Reference
|
||||
-------------
|
||||
|
||||
|
@ -384,3 +384,7 @@
|
||||
-
|
||||
re_variables: ['ESP_HF_CME_MEMEORY_FAILURE']
|
||||
hint_variables: ['ESP_HF_CME_MEMEORY_FAILURE', 'ESP_HF_CME_MEMORY_FAILURE ']
|
||||
|
||||
-
|
||||
re: "intr_alloc: No free interrupt inputs for [_\\w]+ interrupt"
|
||||
hint: "For troubleshooting instructions related to interrupt allocation, run 'idf.py docs -sp api-reference/system/intr_alloc.html'"
|
||||
|
5
tools/test_apps/system/esp_intr_dump/CMakeLists.txt
Normal file
5
tools/test_apps/system/esp_intr_dump/CMakeLists.txt
Normal file
@ -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)
|
14
tools/test_apps/system/esp_intr_dump/README.md
Normal file
14
tools/test_apps/system/esp_intr_dump/README.md
Normal file
@ -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`.
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
3
tools/test_apps/system/esp_intr_dump/main/CMakeLists.txt
Normal file
3
tools/test_apps/system/esp_intr_dump/main/CMakeLists.txt
Normal file
@ -0,0 +1,3 @@
|
||||
idf_component_register(SRCS "test_esp_intr_dump_main.c"
|
||||
PRIV_REQUIRES console
|
||||
INCLUDE_DIRS ".")
|
@ -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));
|
||||
}
|
52
tools/test_apps/system/esp_intr_dump/pytest_esp_intr_dump.py
Normal file
52
tools/test_apps/system/esp_intr_dump/pytest_esp_intr_dump.py
Normal file
@ -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())
|
Loading…
x
Reference in New Issue
Block a user