mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
fix(esp_hw_support): make the NMI interrupts available for the main application
Closes https://github.com/espressif/esp-idf/issues/13629 NMI interrupt level has been freed for all the Xtensa targets, making it possible for the main application to use it. An example has been added to show how to proceed.
This commit is contained in:
parent
966f2c6a5b
commit
928859307f
@ -167,7 +167,7 @@ const static intr_desc_t intr_desc_table [SOC_CPU_INTR_NUM] = {
|
||||
[11] = { 3, ESP_CPU_INTR_TYPE_NA, { ESP_CPU_INTR_DESC_FLAG_SPECIAL, ESP_CPU_INTR_DESC_FLAG_SPECIAL } },
|
||||
[12] = { 1, ESP_CPU_INTR_TYPE_LEVEL, { 0, 0 } },
|
||||
[13] = { 1, ESP_CPU_INTR_TYPE_LEVEL, { 0, 0 } },
|
||||
[14] = { 7, ESP_CPU_INTR_TYPE_LEVEL, { ESP_CPU_INTR_DESC_FLAG_RESVD, ESP_CPU_INTR_DESC_FLAG_RESVD } }, // NMI
|
||||
[14] = { 7, ESP_CPU_INTR_TYPE_LEVEL, { 0, 0 } }, // NMI
|
||||
#if CONFIG_FREERTOS_CORETIMER_1
|
||||
[15] = { 3, ESP_CPU_INTR_TYPE_NA, { ESP_CPU_INTR_DESC_FLAG_RESVD, ESP_CPU_INTR_DESC_FLAG_RESVD } },
|
||||
#else
|
||||
|
@ -44,7 +44,7 @@ const static intr_desc_t intr_desc_table [SOC_CPU_INTR_NUM] = {
|
||||
[12] = { 1, ESP_CPU_INTR_TYPE_LEVEL, 0 },
|
||||
[13] = { 1, ESP_CPU_INTR_TYPE_LEVEL, 0 },
|
||||
/* Interrupt 14 reserved for NMI (Non-Maskable Interrupts) */
|
||||
[14] = { 7, ESP_CPU_INTR_TYPE_LEVEL, ESP_CPU_INTR_DESC_FLAG_RESVD },
|
||||
[14] = { 7, ESP_CPU_INTR_TYPE_LEVEL, 0 },
|
||||
#if CONFIG_FREERTOS_CORETIMER_1
|
||||
[15] = { 3, ESP_CPU_INTR_TYPE_NA, ESP_CPU_INTR_DESC_FLAG_RESVD },
|
||||
#else
|
||||
|
@ -42,7 +42,7 @@ const static intr_desc_t intr_desc_table [SOC_CPU_INTR_NUM] = {
|
||||
[12] = { 1, ESP_CPU_INTR_TYPE_LEVEL, { 0, 0 } },
|
||||
[13] = { 1, ESP_CPU_INTR_TYPE_LEVEL, { 0, 0 } },
|
||||
/* Interrupt 14 reserved for NMI (Non-Maskable Interrupts) */
|
||||
[14] = { 7, ESP_CPU_INTR_TYPE_LEVEL, { ESP_CPU_INTR_DESC_FLAG_RESVD, ESP_CPU_INTR_DESC_FLAG_RESVD } }, // NMI
|
||||
[14] = { 7, ESP_CPU_INTR_TYPE_LEVEL, { 0, 0 } }, // NMI
|
||||
[15] = { 3, ESP_CPU_INTR_TYPE_NA, { ESP_CPU_INTR_DESC_FLAG_SPECIAL, ESP_CPU_INTR_DESC_FLAG_SPECIAL } },
|
||||
[16] = { 5, ESP_CPU_INTR_TYPE_NA, { ESP_CPU_INTR_DESC_FLAG_SPECIAL, ESP_CPU_INTR_DESC_FLAG_SPECIAL } },
|
||||
[17] = { 1, ESP_CPU_INTR_TYPE_LEVEL, { 0, 0 } },
|
||||
|
@ -119,3 +119,5 @@ This will ensure the linker to always includes the file defining ``ld_include_my
|
||||
- In theory, medium priority interrupts could also be handled in this way. ESP-IDF does not support this yet.
|
||||
|
||||
- To check Xtensa instruction set architecture (ISA), please refer to `Xtensa ISA Summary <https://www.cadence.com/content/dam/cadence-www/global/en_US/documents/tools/ip/tensilica-ip/isa-summary.pdf>`_.
|
||||
|
||||
See :example:`system/nmi_isr` for an example of how to implement a custom NMI handler on Xtensa-based targets.
|
||||
|
@ -139,6 +139,11 @@ examples/system/light_sleep:
|
||||
disable:
|
||||
- if: SOC_LIGHT_SLEEP_SUPPORTED != 1
|
||||
|
||||
examples/system/nmi_isr:
|
||||
enable:
|
||||
- if: IDF_TARGET_ARCH_XTENSA == 1
|
||||
reason: test NMI for Xtensa targets only
|
||||
|
||||
examples/system/ota/advanced_https_ota:
|
||||
disable:
|
||||
- if: IDF_TARGET in ["esp32h2", "esp32c61"]
|
||||
|
6
examples/system/nmi_isr/CMakeLists.txt
Normal file
6
examples/system/nmi_isr/CMakeLists.txt
Normal file
@ -0,0 +1,6 @@
|
||||
# The following lines of boilerplate have to be in your project's CMakeLists
|
||||
# in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(nmi_isr)
|
40
examples/system/nmi_isr/README.md
Normal file
40
examples/system/nmi_isr/README.md
Normal file
@ -0,0 +1,40 @@
|
||||
| Supported Targets | ESP32 | ESP32-S2 | ESP32-S3 |
|
||||
| ----------------- | ----- | -------- | -------- |
|
||||
|
||||
# NMI ISR Example
|
||||
|
||||
This example demonstrates how to allocate and use non-maskable interrupt (NMI) on Xtensa-based targets. The `asm_funcs.S` file contains the ISR that will be run on the core that installed the NMI. The callback should be fairly simple and must be entirely written in assembly.
|
||||
|
||||
Defining an NMI handler can be done by defining a routine named `xt_nmi`. That routine will be called via `call0` instruction, as such, before returning from the ISR, the return address register, `a0`, must be restored thanks to the instruction:
|
||||
|
||||
```
|
||||
rsr a0, EXCSAVE + XCHAL_NMILEVEL
|
||||
```
|
||||
|
||||
## How to use example
|
||||
|
||||
### Hardware Required
|
||||
|
||||
Example can run on any Xtensa-based ESP32 development board. Since the example uses GPIO19 as a bi-directional pin, make sure not to connect it to anything.
|
||||
|
||||
### Configure the project
|
||||
|
||||
No particular configuration is required to run this example, the default one is suitable.
|
||||
|
||||
### Build and Flash
|
||||
|
||||
```
|
||||
idf.py build flash monitor
|
||||
```
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/latest/get-started/index.html) for full steps to configure and use ESP-IDF to build projects.
|
||||
|
||||
|
||||
## Example output
|
||||
|
||||
```
|
||||
example: Start
|
||||
example: Success
|
||||
```
|
4
examples/system/nmi_isr/main/CMakeLists.txt
Normal file
4
examples/system/nmi_isr/main/CMakeLists.txt
Normal file
@ -0,0 +1,4 @@
|
||||
idf_component_register(SRCS "nmi_isr_main.c"
|
||||
"asm_funcs.S"
|
||||
INCLUDE_DIRS "."
|
||||
WHOLE_ARCHIVE)
|
47
examples/system/nmi_isr/main/asm_funcs.S
Normal file
47
examples/system/nmi_isr/main/asm_funcs.S
Normal file
@ -0,0 +1,47 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <xtensa/coreasm.h>
|
||||
#include "soc/gpio_reg.h"
|
||||
#include "example_gpio.h"
|
||||
|
||||
.global nmi_triggered
|
||||
|
||||
.section .bss
|
||||
nmi_triggered:
|
||||
.space 4
|
||||
|
||||
|
||||
/**
|
||||
* @brief This current ISR was called via `call0` instruction, so `a0` (return address)
|
||||
* was altered. Fortunately, `a0` was saved in EXCSAVE registers, restore it before
|
||||
* returning
|
||||
*/
|
||||
.section .iram1, "ax"
|
||||
.align 4
|
||||
.global xt_nmi
|
||||
.type xt_nmi, @function
|
||||
xt_nmi:
|
||||
addi sp, sp, -16
|
||||
s32i a3, sp, 0
|
||||
|
||||
/* Set the interrupt flag to 1 */
|
||||
movi a0, nmi_triggered
|
||||
movi a3, 1
|
||||
s32i a3, a0, 0
|
||||
|
||||
/* Set the GPIO level back to low to prevent triggering an interrupt again */
|
||||
movi a0, GPIO_OUT_W1TC_REG
|
||||
movi a3, 1 << EXAMPLE_GPIO_IN
|
||||
s32i a3, a0, 0
|
||||
|
||||
/* Restore a3 and a0 before leaving*/
|
||||
l32i a3, sp, 0
|
||||
addi sp, sp, 16
|
||||
rsr a0, EXCSAVE + XCHAL_NMILEVEL
|
||||
|
||||
/* Return from NMI, we need to specify the level */
|
||||
rfi XCHAL_NMILEVEL
|
9
examples/system/nmi_isr/main/example_gpio.h
Normal file
9
examples/system/nmi_isr/main/example_gpio.h
Normal file
@ -0,0 +1,9 @@
|
||||
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#define EXAMPLE_GPIO_IN 19
|
63
examples/system/nmi_isr/main/nmi_isr_main.c
Normal file
63
examples/system/nmi_isr/main/nmi_isr_main.c
Normal file
@ -0,0 +1,63 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <inttypes.h>
|
||||
#include "freertos/FreeRTOSConfig.h"
|
||||
#include "freertos/FreeRTOS.h"
|
||||
#include "sdkconfig.h"
|
||||
#include "driver/gpio.h"
|
||||
#include "hal/gpio_ll.h"
|
||||
#include "soc/interrupts.h"
|
||||
#include "example_gpio.h"
|
||||
|
||||
extern volatile int nmi_triggered;
|
||||
|
||||
extern void xt_nmi(void*);
|
||||
|
||||
void app_main(void)
|
||||
{
|
||||
intr_handle_t handle;
|
||||
esp_err_t err;
|
||||
|
||||
printf("example: Start\n");
|
||||
|
||||
/* Make sure we have a pull-down on the input GPIO to prevent noise (when disconnected) */
|
||||
gpio_pulldown_en(EXAMPLE_GPIO_IN);
|
||||
gpio_set_direction(EXAMPLE_GPIO_IN, GPIO_MODE_INPUT_OUTPUT);
|
||||
|
||||
/* Register the interrupt handler as an NMI. When registering high level interrupts,
|
||||
* the interrupt allocator expects the handler passed as an argument to be NULL. */
|
||||
err = esp_intr_alloc(ETS_GPIO_INTR_SOURCE, ESP_INTR_FLAG_IRAM | ESP_INTR_FLAG_NMI, NULL, NULL, &handle);
|
||||
if (err != ESP_OK) {
|
||||
printf("Failure: could not install NMI ISR, %d(0x%x)\n", err, err);
|
||||
return;
|
||||
}
|
||||
gpio_set_intr_type(EXAMPLE_GPIO_IN, GPIO_INTR_HIGH_LEVEL);
|
||||
gpio_intr_enable(EXAMPLE_GPIO_IN);
|
||||
|
||||
vTaskDelay(200 / portTICK_PERIOD_MS);
|
||||
|
||||
/* Disable interrupts on the CPU side and make sure the NMI is still triggered */
|
||||
const uint32_t mask = esp_cpu_intr_get_enabled_mask();
|
||||
esp_cpu_intr_disable(0xFFFFFFFF);
|
||||
nmi_triggered = 0;
|
||||
|
||||
/* Setting EXAMPLE_GPIO_IN to 1 will trigger the NMI interrupt. */
|
||||
gpio_set_level(EXAMPLE_GPIO_IN, 1);
|
||||
|
||||
/* Wait for the interrupt to occur */
|
||||
while (nmi_triggered == 0) {
|
||||
/* We cannot use vTaskDelay since the interrupts are disabled */
|
||||
}
|
||||
|
||||
esp_cpu_intr_enable(mask);
|
||||
|
||||
gpio_intr_disable(EXAMPLE_GPIO_IN);
|
||||
esp_intr_free(handle);
|
||||
printf("example: Success\n");
|
||||
}
|
13
examples/system/nmi_isr/pytest_nmi_isr.py
Normal file
13
examples/system/nmi_isr/pytest_nmi_isr.py
Normal file
@ -0,0 +1,13 @@
|
||||
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: CC0-1.0
|
||||
import pytest
|
||||
from pytest_embedded import Dut
|
||||
|
||||
|
||||
@pytest.mark.esp32
|
||||
@pytest.mark.esp32s2
|
||||
@pytest.mark.esp32s3
|
||||
@pytest.mark.generic
|
||||
def test_nmi_isr(dut: Dut) -> None:
|
||||
dut.expect_exact('example: Start')
|
||||
dut.expect_exact('example: Success')
|
0
examples/system/nmi_isr/sdkconfig.defaults
Normal file
0
examples/system/nmi_isr/sdkconfig.defaults
Normal file
@ -14,7 +14,7 @@ CPU 0 interrupt status:
|
||||
11 3 Level CPU-internal
|
||||
12 1 Level Free
|
||||
13 1 Level Free
|
||||
14 7 Level Reserved
|
||||
14 7 Level Free
|
||||
15 3 Level CPU-internal
|
||||
16 5 Level CPU-internal
|
||||
17 1 Level Free
|
||||
@ -48,7 +48,7 @@ CPU 1 interrupt status:
|
||||
11 3 Level CPU-internal
|
||||
12 1 Level Free
|
||||
13 1 Level Free
|
||||
14 7 Level Reserved
|
||||
14 7 Level Free
|
||||
15 3 Level CPU-internal
|
||||
16 5 Level CPU-internal
|
||||
17 1 Level Free
|
||||
|
@ -14,7 +14,7 @@ CPU 0 interrupt status:
|
||||
11 3 Level CPU-internal
|
||||
12 1 Level Free
|
||||
13 1 Level Free
|
||||
14 7 Level Reserved
|
||||
14 7 Level Free
|
||||
15 3 Level CPU-internal
|
||||
16 5 Level CPU-internal
|
||||
17 1 Level Free
|
||||
|
@ -14,7 +14,7 @@ CPU 0 interrupt status:
|
||||
11 3 Level CPU-internal
|
||||
12 1 Level Free
|
||||
13 1 Level Free
|
||||
14 7 Level Reserved
|
||||
14 7 Level Free
|
||||
15 3 Level CPU-internal
|
||||
16 5 Level CPU-internal
|
||||
17 1 Level Free
|
||||
@ -48,7 +48,7 @@ CPU 1 interrupt status:
|
||||
11 3 Level CPU-internal
|
||||
12 1 Level Free
|
||||
13 1 Level Free
|
||||
14 7 Level Reserved
|
||||
14 7 Level Free
|
||||
15 3 Level CPU-internal
|
||||
16 5 Level CPU-internal
|
||||
17 1 Level Free
|
||||
|
Loading…
Reference in New Issue
Block a user