diff --git a/examples/system/.build-test-rules.yml b/examples/system/.build-test-rules.yml index bfac822ab9..6983c3bec6 100644 --- a/examples/system/.build-test-rules.yml +++ b/examples/system/.build-test-rules.yml @@ -360,6 +360,12 @@ examples/system/ulp/ulp_riscv/i2c: depends_components: - ulp +examples/system/ulp/ulp_riscv/interrupts: + enable: + - if: SOC_RISCV_COPROC_SUPPORTED == 1 + depends_components: + - ulp + examples/system/ulp/ulp_riscv/touch: enable: - if: SOC_RISCV_COPROC_SUPPORTED == 1 diff --git a/examples/system/ulp/ulp_riscv/interrupts/CMakeLists.txt b/examples/system/ulp/ulp_riscv/interrupts/CMakeLists.txt new file mode 100644 index 0000000000..8a6cce23ac --- /dev/null +++ b/examples/system/ulp/ulp_riscv/interrupts/CMakeLists.txt @@ -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(ulp_riscv_interrupts) diff --git a/examples/system/ulp/ulp_riscv/interrupts/README.md b/examples/system/ulp/ulp_riscv/interrupts/README.md new file mode 100644 index 0000000000..3058b0bd92 --- /dev/null +++ b/examples/system/ulp/ulp_riscv/interrupts/README.md @@ -0,0 +1,71 @@ +| Supported Targets | ESP32-S2 | ESP32-S3 | +| ----------------- | -------- | -------- | + +# ULP-RISC-V Interrupt Handling: + +This example demonstrates how the ULP-RISC-V coprocessor can register and handle interrupts. Currently this example supports handling of - +- Software triggered interrupts +- RTC IO triggered interrupts + +The example keeps a count of the software interrupts triggered on the ULP RISC-V core and wakes up the main processor from deep sleep after a certain threshold. +Additionaly, it wakes up the main processor from deep sleep when a button is pressed and the GPIO interrupt is triggered. + +## How to use example + +### Hardware Required + +The example can be run on any ESP32-S2 or ESP32-S3 based development board connected to a computer with a single USB cable for flashing and monitoring. The external interface should have 3.3V outputs. You may use e.g. 3.3V compatible USB-to-Serial dongle. + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT flash monitor +``` + +(To exit the serial monitor, type ``Ctrl-]``.) + +By default, this example uses GPIO#0 which is connected to the "Boot" button on esp32-s2/s3 development boards. To trigger the GPIO interrupt on the ULP RISC-V core, press the Boot button. + +## Example Output + +Output from main CPU: + +``` +Not a ULP RISC-V wakeup, initializing it! +Entering in deep sleep + +ESP-ROM:esp32s3-20210327 +Build:Mar 27 2021 +rst:0x5 (DSLEEP),boot:0x8 (SPI_FAST_FLASH_BOOT) +pro cpu reset by JTAG +SPIWP:0xee +mode:DIO, clock div:1 +load:0x3fce3810,len:0x12b4 +load:0x403c9700,len:0x4 +load:0x403c9704,len:0xaf4 +load:0x403cc700,len:0x2bcc +entry 0x403c9898 +W (91) spi_flash: Detected size(8192k) larger than the size in the binary image header(2048k). Using the size in the binary image header. +ULP RISC-V woke up the main CPU! +ULP RISC-V SW Interrupt triggered 5 times. +Entering in deep sleep + +ESP-ROM:esp32s3-20210327 +Build:Mar 27 2021 +rst:0x5 (DSLEEP),boot:0x8 (SPI_FAST_FLASH_BOOT) +pro cpu reset by JTAG +SPIWP:0xee +mode:DIO, clock div:1 +load:0x3fce3810,len:0x12b4 +load:0x403c9700,len:0x4 +load:0x403c9704,len:0xaf4 +load:0x403cc700,len:0x2bcc +entry 0x403c9898 +W (91) spi_flash: Detected size(8192k) larger than the size in the binary image header(2048k). Using the size in the binary image header. +ULP RISC-V woke up the main CPU! +ULP RISC-V GPIO Interrupt triggered. +Entering in deep sleep + +``` diff --git a/examples/system/ulp/ulp_riscv/interrupts/main/CMakeLists.txt b/examples/system/ulp/ulp_riscv/interrupts/main/CMakeLists.txt new file mode 100644 index 0000000000..7dde1dd9b4 --- /dev/null +++ b/examples/system/ulp/ulp_riscv/interrupts/main/CMakeLists.txt @@ -0,0 +1,27 @@ +# Set usual component variables +set(COMPONENT_SRCS "ulp_riscv_example_main.c") +set(COMPONENT_ADD_INCLUDEDIRS "") +set(COMPONENT_REQUIRES ulp) + +register_component() + +# +# ULP support additions to component CMakeLists.txt. +# +# 1. The ULP app name must be unique (if multiple components use ULP). +set(ulp_app_name ulp_${COMPONENT_NAME}) +# +# 2. Specify all C and Assembly source files. +# Files should be placed into a separate directory (in this case, ulp/), +# which should not be added to COMPONENT_SRCS. +set(ulp_riscv_sources "ulp/main.c") + +# +# 3. List all the component source files which include automatically +# generated ULP export file, ${ulp_app_name}.h: +set(ulp_exp_dep_srcs "ulp_riscv_example_main.c") + +# +# 4. Call function to build ULP binary and embed in project using the argument +# values above. +ulp_embed_binary(${ulp_app_name} "${ulp_riscv_sources}" "${ulp_exp_dep_srcs}") diff --git a/examples/system/ulp/ulp_riscv/interrupts/main/Kconfig.projbuild b/examples/system/ulp/ulp_riscv/interrupts/main/Kconfig.projbuild new file mode 100644 index 0000000000..4ffdf59111 --- /dev/null +++ b/examples/system/ulp/ulp_riscv/interrupts/main/Kconfig.projbuild @@ -0,0 +1,10 @@ +menu "ULP RISC-V Interrupt Example Configuration" + + config EXAMPLE_GPIO_INT + int "GPIO interrupt pin" + range 0 21 + default 0 + help + GPIO number to trigger an interrupt on the ULP RISC-V core. + +endmenu diff --git a/examples/system/ulp/ulp_riscv/interrupts/main/ulp/main.c b/examples/system/ulp/ulp_riscv/interrupts/main/ulp/main.c new file mode 100644 index 0000000000..09731c85c2 --- /dev/null +++ b/examples/system/ulp/ulp_riscv/interrupts/main/ulp/main.c @@ -0,0 +1,63 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +/* ULP RISC-V interrupts example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. + + This code runs on ULP RISC-V coprocessor +*/ + +#include "ulp_riscv_utils.h" +#include "ulp_riscv_gpio.h" + +#define WAKE_PIN CONFIG_EXAMPLE_GPIO_INT + +int sw_int_cnt = 0; +int wake_by_sw = 0; +int wake_by_gpio = 0; + +/* SW Interrupt Handler */ +void sw_int_handler(void *arg) +{ + sw_int_cnt++; +} + +/* GPIO Interrupt Handler */ +void gpio_int_handler(void *arg) +{ + wake_by_gpio = 1; + ulp_riscv_wakeup_main_processor(); +} + +int main(void) { + /* Register SW interrupt handler */ + ulp_riscv_enable_sw_intr(sw_int_handler, NULL); + + /* Configure GPIO in input mode for interrupt */ + ulp_riscv_gpio_init(WAKE_PIN); + ulp_riscv_gpio_input_enable(WAKE_PIN); + + /* Register GPIO interrupt handler */ + ulp_riscv_gpio_isr_register(WAKE_PIN, ULP_RISCV_GPIO_INTR_POSEDGE, gpio_int_handler, NULL); + + while (1) { + /* Trigger SW interrupt */ + ulp_riscv_trigger_sw_intr(); + + if ((sw_int_cnt % 5 == 0) && !wake_by_gpio) { + wake_by_sw = 1; + ulp_riscv_wakeup_main_processor(); + } + + ulp_riscv_delay_cycles(1000 * ULP_RISCV_CYCLES_PER_MS); + } + + return 0; +} diff --git a/examples/system/ulp/ulp_riscv/interrupts/main/ulp_riscv_example_main.c b/examples/system/ulp/ulp_riscv/interrupts/main/ulp_riscv_example_main.c new file mode 100644 index 0000000000..446bb62d97 --- /dev/null +++ b/examples/system/ulp/ulp_riscv/interrupts/main/ulp_riscv_example_main.c @@ -0,0 +1,71 @@ +/* + * SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +/* ULP RISC-V interrupts example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include "esp_sleep.h" +#include "ulp_riscv.h" +#include "ulp_main.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start"); +extern const uint8_t ulp_main_bin_end[] asm("_binary_ulp_main_bin_end"); + +static void init_ulp_program(void); + +void app_main(void) +{ + esp_sleep_wakeup_cause_t cause = esp_sleep_get_wakeup_cause(); + /* not a wakeup from ULP, load the firmware */ + if (cause != ESP_SLEEP_WAKEUP_ULP) { + printf("Not a ULP RISC-V wakeup, initializing it! \n"); + init_ulp_program(); + } + + /* ULP RISC-V handled an interrupt on GPIO#0 */ + if (cause == ESP_SLEEP_WAKEUP_ULP) { + printf("ULP RISC-V woke up the main CPU! \n"); + if (ulp_wake_by_sw) { + printf("ULP RISC-V SW Interrupt triggered %lu times.\r\n", ulp_sw_int_cnt); + ulp_wake_by_sw = 0; + } else if (ulp_wake_by_gpio) { + printf("ULP RISC-V GPIO Interrupt triggered.\r\n"); + ulp_wake_by_gpio = 0; + } + } + + /* Go back to sleep, only the ULP RISC-V will run */ + printf("Entering in deep sleep\n\n"); + + /* Small delay to ensure the messages are printed */ + vTaskDelay(100); + + ESP_ERROR_CHECK( esp_sleep_enable_ulp_wakeup()); + esp_deep_sleep_start(); +} + +static void init_ulp_program(void) +{ + esp_err_t err = ulp_riscv_load_binary(ulp_main_bin_start, (ulp_main_bin_end - ulp_main_bin_start)); + ESP_ERROR_CHECK(err); + + /* The first argument is the period index, which is not used by the ULP-RISC-V timer + * The second argument is the period in microseconds, which gives a wakeup time period of: 20ms + */ + ulp_set_wakeup_period(0, 20000); + + /* Start the program */ + err = ulp_riscv_run(); + ESP_ERROR_CHECK(err); +} diff --git a/examples/system/ulp/ulp_riscv/interrupts/pytest_ulp_riscv_interrupts.py b/examples/system/ulp/ulp_riscv/interrupts/pytest_ulp_riscv_interrupts.py new file mode 100644 index 0000000000..c0bf9928ae --- /dev/null +++ b/examples/system/ulp/ulp_riscv/interrupts/pytest_ulp_riscv_interrupts.py @@ -0,0 +1,30 @@ +# SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 + +import time + +import pytest +from pytest_embedded import Dut + + +@pytest.mark.esp32s2 +@pytest.mark.esp32s3 +@pytest.mark.generic +def test_ulp_riscv_interrupts(dut: Dut) -> None: + + dut.expect_exact('Not a ULP RISC-V wakeup, initializing it!') + dut.expect_exact('Entering in deep sleep') + + # Give the chip time to enter deepsleep and trigger a software interrupt + time.sleep(5) + + # Verify if SW interrupt got triggered + dut.expect_exact('ULP RISC-V SW Interrupt triggered 5 times.') + + # Set GPIO#0 using DTR + dut.serial.proc.setDTR(True) + time.sleep(1) + dut.serial.proc.setDTR(False) + + # Verify if GPIO interrupt got triggered + dut.expect_exact('ULP RISC-V GPIO Interrupt triggered.', timeout=5) diff --git a/examples/system/ulp/ulp_riscv/interrupts/sdkconfig.defaults b/examples/system/ulp/ulp_riscv/interrupts/sdkconfig.defaults new file mode 100644 index 0000000000..e3745e5057 --- /dev/null +++ b/examples/system/ulp/ulp_riscv/interrupts/sdkconfig.defaults @@ -0,0 +1,9 @@ +# Enable ULP +CONFIG_ULP_COPROC_ENABLED=y +CONFIG_ULP_COPROC_TYPE_RISCV=y +CONFIG_ULP_COPROC_RESERVE_MEM=4096 +# Set log level to Warning to produce clean output +CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y +CONFIG_BOOTLOADER_LOG_LEVEL=2 +CONFIG_LOG_DEFAULT_LEVEL_WARN=y +CONFIG_LOG_DEFAULT_LEVEL=2