Skip to content

Commit

Permalink
fix(esp_hw_support): make the NMI interrupts available for the main a…
Browse files Browse the repository at this point in the history
…pplication

Closes #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.
  • Loading branch information
o-marshmallow committed Aug 29, 2024
1 parent 966f2c6 commit 9288593
Show file tree
Hide file tree
Showing 16 changed files with 197 additions and 8 deletions.
2 changes: 1 addition & 1 deletion components/esp_hw_support/port/esp32/esp_cpu_intr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion components/esp_hw_support/port/esp32s2/esp_cpu_intr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion components/esp_hw_support/port/esp32s3/esp_cpu_intr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 } },
Expand Down
2 changes: 2 additions & 0 deletions docs/en/api-guides/hlinterrupts.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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.
5 changes: 5 additions & 0 deletions examples/system/.build-test-rules.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
6 changes: 6 additions & 0 deletions examples/system/nmi_isr/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -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 changes: 40 additions & 0 deletions examples/system/nmi_isr/README.md
Original file line number Diff line number Diff line change
@@ -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 changes: 4 additions & 0 deletions examples/system/nmi_isr/main/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
idf_component_register(SRCS "nmi_isr_main.c"
"asm_funcs.S"
INCLUDE_DIRS "."
WHOLE_ARCHIVE)
47 changes: 47 additions & 0 deletions examples/system/nmi_isr/main/asm_funcs.S
Original file line number Diff line number Diff line change
@@ -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 changes: 9 additions & 0 deletions examples/system/nmi_isr/main/example_gpio.h
Original file line number Diff line number Diff line change
@@ -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 changes: 63 additions & 0 deletions examples/system/nmi_isr/main/nmi_isr_main.c
Original file line number Diff line number Diff line change
@@ -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 changes: 13 additions & 0 deletions examples/system/nmi_isr/pytest_nmi_isr.py
Original file line number Diff line number Diff line change
@@ -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')
Empty file.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit 9288593

Please sign in to comment.