From a9eba4058e3a9950cd5899de23f08a5b1f34c5ee Mon Sep 17 00:00:00 2001 From: jath03 Date: Tue, 8 Apr 2025 19:04:41 -0700 Subject: [PATCH 1/2] feat(ulp): LP Timer interrupt support This commit adds support for the LP Timer interrupt to be used by the LP Core. Merges https://github.com/espressif/esp-idf/pull/15717 --- components/hal/esp32c5/include/hal/lp_timer_ll.h | 5 +++++ components/hal/esp32c6/include/hal/lp_timer_ll.h | 7 ++++++- components/hal/esp32c61/include/hal/lp_timer_ll.h | 5 +++++ components/hal/esp32h2/include/hal/lp_timer_ll.h | 5 +++++ components/hal/esp32p4/include/hal/lp_timer_ll.h | 5 +++++ .../lp_core/lp_core/include/ulp_lp_core_utils.h | 14 ++++++++++++++ components/ulp/lp_core/lp_core/lp_core_utils.c | 12 ++++++++++++ 7 files changed, 52 insertions(+), 1 deletion(-) diff --git a/components/hal/esp32c5/include/hal/lp_timer_ll.h b/components/hal/esp32c5/include/hal/lp_timer_ll.h index cac0a289b5..6e8b35d6af 100644 --- a/components/hal/esp32c5/include/hal/lp_timer_ll.h +++ b/components/hal/esp32c5/include/hal/lp_timer_ll.h @@ -73,6 +73,11 @@ FORCE_INLINE_ATTR void lp_timer_ll_clear_lp_intsts_mask(lp_timer_dev_t *dev, uin dev->lp_int_clr.val = mask; } +FORCE_INLINE_ATTR void lp_timer_ll_lp_alarm_intr_enable(lp_timer_dev_t *dev, bool enable) +{ + dev->lp_int_ena.main_timer_lp_int_ena = enable; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32c6/include/hal/lp_timer_ll.h b/components/hal/esp32c6/include/hal/lp_timer_ll.h index 080555bd89..a43a57a684 100644 --- a/components/hal/esp32c6/include/hal/lp_timer_ll.h +++ b/components/hal/esp32c6/include/hal/lp_timer_ll.h @@ -1,5 +1,5 @@ /* - * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * SPDX-FileCopyrightText: 2023-2025 Espressif Systems (Shanghai) CO LTD * * SPDX-License-Identifier: Apache-2.0 */ @@ -73,6 +73,11 @@ FORCE_INLINE_ATTR void lp_timer_ll_clear_lp_intsts_mask(lp_timer_dev_t *dev, uin dev->lp_int_clr.val = mask; } +FORCE_INLINE_ATTR void lp_timer_ll_lp_alarm_intr_enable(lp_timer_dev_t *dev, bool enable) +{ + dev->lp_int_en.alarm = enable; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32c61/include/hal/lp_timer_ll.h b/components/hal/esp32c61/include/hal/lp_timer_ll.h index 1817542336..291b0b3bd7 100644 --- a/components/hal/esp32c61/include/hal/lp_timer_ll.h +++ b/components/hal/esp32c61/include/hal/lp_timer_ll.h @@ -73,6 +73,11 @@ FORCE_INLINE_ATTR void lp_timer_ll_clear_lp_intsts_mask(lp_timer_dev_t *dev, uin dev->lp_int_clr.val = mask; } +FORCE_INLINE_ATTR void lp_timer_ll_lp_alarm_intr_enable(lp_timer_dev_t *dev, bool enable) +{ + dev->lp_int_ena.main_timer_lp_int_ena = enable; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32h2/include/hal/lp_timer_ll.h b/components/hal/esp32h2/include/hal/lp_timer_ll.h index c8e6e2a054..d36788f15c 100644 --- a/components/hal/esp32h2/include/hal/lp_timer_ll.h +++ b/components/hal/esp32h2/include/hal/lp_timer_ll.h @@ -57,6 +57,11 @@ FORCE_INLINE_ATTR void lp_timer_ll_clear_overflow_intr_status(lp_timer_dev_t *de dev->int_clr.overflow = 1; } +FORCE_INLINE_ATTR void lp_timer_ll_lp_alarm_intr_enable(lp_timer_dev_t *dev, bool enable) +{ + dev->lp_int_en.alarm = enable; +} + #ifdef __cplusplus } #endif diff --git a/components/hal/esp32p4/include/hal/lp_timer_ll.h b/components/hal/esp32p4/include/hal/lp_timer_ll.h index 45e8ad3fa8..8ebe6d041c 100644 --- a/components/hal/esp32p4/include/hal/lp_timer_ll.h +++ b/components/hal/esp32p4/include/hal/lp_timer_ll.h @@ -73,6 +73,11 @@ FORCE_INLINE_ATTR void lp_timer_ll_clear_lp_intsts_mask(lp_timer_dev_t *dev, uin dev->lp_int_clr.val = mask; } +FORCE_INLINE_ATTR void lp_timer_ll_lp_alarm_intr_enable(lp_timer_dev_t *dev, bool enable) +{ + dev->lp_int_ena.main_timer_lp_int_ena = enable; +} + #ifdef __cplusplus } #endif diff --git a/components/ulp/lp_core/lp_core/include/ulp_lp_core_utils.h b/components/ulp/lp_core/lp_core/include/ulp_lp_core_utils.h index 48ab1529ba..6279136e8c 100644 --- a/components/ulp/lp_core/lp_core/include/ulp_lp_core_utils.h +++ b/components/ulp/lp_core/lp_core/include/ulp_lp_core_utils.h @@ -104,6 +104,20 @@ void ulp_lp_core_sw_intr_enable(bool enable); */ void ulp_lp_core_sw_intr_clear(void); +#if SOC_LP_TIMER_SUPPORTED +/** + * @brief Enable the LP Timer interrupt + * + */ +void ulp_lp_core_lp_timer_intr_enable(bool enable); + +/** + * @brief Clear the interrupt status for the LP Timer interrupt + * + */ +void ulp_lp_core_lp_timer_intr_clear(void); +#endif + /** * @brief Puts the CPU into a wait state until an interrupt is triggered * diff --git a/components/ulp/lp_core/lp_core/lp_core_utils.c b/components/ulp/lp_core/lp_core/lp_core_utils.c index 7bada098ca..ddf391a17d 100644 --- a/components/ulp/lp_core/lp_core/lp_core_utils.c +++ b/components/ulp/lp_core/lp_core/lp_core_utils.c @@ -191,6 +191,18 @@ void ulp_lp_core_sw_intr_clear(void) pmu_ll_lp_clear_sw_intr_status(&PMU); } +#if SOC_LP_TIMER_SUPPORTED +void ulp_lp_core_lp_timer_intr_enable(bool enable) +{ + lp_timer_ll_lp_alarm_intr_enable(&LP_TIMER, enable); +} + +void ulp_lp_core_lp_timer_intr_clear(void) +{ + lp_timer_ll_clear_lp_alarm_intr_status(&LP_TIMER); +} +#endif + void ulp_lp_core_wait_for_intr(void) { asm volatile("wfi"); From 954d12955da60b3ef7f8402b58560b43e039da12 Mon Sep 17 00:00:00 2001 From: jath03 Date: Thu, 17 Apr 2025 11:54:34 -0700 Subject: [PATCH 2/2] feat(ulp): LP Timer interrupt example This commit adds an example to demonstrate the use of the LP Timer interrupt on the LP Core. --- examples/system/.build-test-rules.yml | 6 +++ .../lp_core/lp_timer_interrupt/CMakeLists.txt | 10 ++++ .../ulp/lp_core/lp_timer_interrupt/README.md | 19 +++++++ .../lp_timer_interrupt/main/CMakeLists.txt | 25 +++++++++ .../lp_timer_interrupt/main/lp_core/main.c | 33 ++++++++++++ .../main/lp_interrupts_main.c | 51 +++++++++++++++++++ .../pytest_lp_timer_interrupt.py | 22 ++++++++ .../lp_timer_interrupt/sdkconfig.defaults | 4 ++ 8 files changed, 170 insertions(+) create mode 100644 examples/system/ulp/lp_core/lp_timer_interrupt/CMakeLists.txt create mode 100644 examples/system/ulp/lp_core/lp_timer_interrupt/README.md create mode 100644 examples/system/ulp/lp_core/lp_timer_interrupt/main/CMakeLists.txt create mode 100644 examples/system/ulp/lp_core/lp_timer_interrupt/main/lp_core/main.c create mode 100644 examples/system/ulp/lp_core/lp_timer_interrupt/main/lp_interrupts_main.c create mode 100644 examples/system/ulp/lp_core/lp_timer_interrupt/pytest_lp_timer_interrupt.py create mode 100644 examples/system/ulp/lp_core/lp_timer_interrupt/sdkconfig.defaults diff --git a/examples/system/.build-test-rules.yml b/examples/system/.build-test-rules.yml index 381c500103..35b75c0cb8 100644 --- a/examples/system/.build-test-rules.yml +++ b/examples/system/.build-test-rules.yml @@ -334,6 +334,12 @@ examples/system/ulp/lp_core/lp_spi: depends_components: - ulp +examples/system/ulp/lp_core/lp_timer_interrupt: + disable: + - if: (SOC_LP_CORE_SUPPORTED != 1) or (SOC_LP_TIMER_SUPPORTED != 1) + depends_components: + - ulp + examples/system/ulp/lp_core/lp_touch: enable: - if: SOC_TOUCH_SENSOR_SUPPORTED == 1 and (SOC_DEEP_SLEEP_SUPPORTED == 1 and SOC_LP_CORE_SUPPORTED == 1) diff --git a/examples/system/ulp/lp_core/lp_timer_interrupt/CMakeLists.txt b/examples/system/ulp/lp_core/lp_timer_interrupt/CMakeLists.txt new file mode 100644 index 0000000000..0901afb4af --- /dev/null +++ b/examples/system/ulp/lp_core/lp_timer_interrupt/CMakeLists.txt @@ -0,0 +1,10 @@ +# For more information about build system see +# https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html +# The following five 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) +# "Trim" the build. Include the minimal set of components, main, and anything it depends on. +# idf_build_set_property(MINIMAL_BUILD ON) +project(interrupts) diff --git a/examples/system/ulp/lp_core/lp_timer_interrupt/README.md b/examples/system/ulp/lp_core/lp_timer_interrupt/README.md new file mode 100644 index 0000000000..1434b24a2c --- /dev/null +++ b/examples/system/ulp/lp_core/lp_timer_interrupt/README.md @@ -0,0 +1,19 @@ +| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-P4 | +| ----------------- | -------- | -------- | -------- | + +# LP-Core example with interrupt triggered from LP Timer + +This example demonstrates how to program the ULP coprocessor to receive an interrupt triggered by the LP Timer + +ULP program written in C can be found across `lp_core/main.c`. The build system compiles and links this program, converts it into binary format, and embeds it into the .rodata section of the ESP-IDF application. + +At runtime, the application running inside the main CPU loads ULP program into the `RTC_SLOW_MEM` memory region using `ulp_lp_core_load_binary` function. The main code then configures the ULP and starts the coprocessor by using `ulp_lp_core_run`. Once the ULP program is started, it runs continuously, waiting for interrupts. The main program will periodically trigger interrupts on the LP-Core. + +The main core will continuously read a counter of interrupts received as reported by the LP-Core. + +## Example output + +``` +LP core loaded with firmware and running successfully +Interrupt count: 6 +``` diff --git a/examples/system/ulp/lp_core/lp_timer_interrupt/main/CMakeLists.txt b/examples/system/ulp/lp_core/lp_timer_interrupt/main/CMakeLists.txt new file mode 100644 index 0000000000..6eb2981339 --- /dev/null +++ b/examples/system/ulp/lp_core/lp_timer_interrupt/main/CMakeLists.txt @@ -0,0 +1,25 @@ +# Register the component +idf_component_register(SRCS "lp_interrupts_main.c" + INCLUDE_DIRS "" + REQUIRES ulp) + +# +# ULP support additions to component CMakeLists.txt. +# +# 1. The LP Core app name must be unique (if multiple components use LP Core). +set(ulp_app_name lp_core_${COMPONENT_NAME}) +# +# 2. Specify all C files. +# Files should be placed into a separate directory (in this case, lp_core/), +# which should not be added to COMPONENT_SRCS. +set(ulp_lp_core_sources "lp_core/main.c") + +# +# 3. List all the component source files which include automatically +# generated LP Core export file, ${ulp_app_name}.h: +set(ulp_exp_dep_srcs "lp_interrupts_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_lp_core_sources}" "${ulp_exp_dep_srcs}") diff --git a/examples/system/ulp/lp_core/lp_timer_interrupt/main/lp_core/main.c b/examples/system/ulp/lp_core/lp_timer_interrupt/main/lp_core/main.c new file mode 100644 index 0000000000..9637296f0a --- /dev/null +++ b/examples/system/ulp/lp_core/lp_timer_interrupt/main/lp_core/main.c @@ -0,0 +1,33 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "ulp_lp_core_utils.h" +#include "ulp_lp_core_interrupts.h" +#include "ulp_lp_core_lp_timer_shared.h" + +uint32_t lp_timer_intr_count = 0; + +/* Add LP_CORE_ISR_ATTR to ensure registers are saved and restored */ +void LP_CORE_ISR_ATTR ulp_lp_core_lp_timer_intr_handler(void) +{ + ulp_lp_core_lp_timer_intr_clear(); + lp_timer_intr_count++; + ulp_lp_core_lp_timer_set_wakeup_time(500000); +} + +int main(void) +{ + ulp_lp_core_intr_enable(); + ulp_lp_core_lp_timer_set_wakeup_time(500000); + ulp_lp_core_lp_timer_intr_enable(true); + + while (1) { + /* Wait forever, handling interrupts */ + asm volatile("wfi"); + } + return 0; +} diff --git a/examples/system/ulp/lp_core/lp_timer_interrupt/main/lp_interrupts_main.c b/examples/system/ulp/lp_core/lp_timer_interrupt/main/lp_interrupts_main.c new file mode 100644 index 0000000000..acae284ae9 --- /dev/null +++ b/examples/system/ulp/lp_core/lp_timer_interrupt/main/lp_interrupts_main.c @@ -0,0 +1,51 @@ +/* + * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#include +#include "esp_sleep.h" +#include "esp_err.h" +#include "lp_core_main.h" +#include "ulp_lp_core.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +extern const uint8_t lp_core_main_bin_start[] asm("_binary_lp_core_main_bin_start"); +extern const uint8_t lp_core_main_bin_end[] asm("_binary_lp_core_main_bin_end"); + +static void lp_core_init(void) +{ + /* Set LP core wakeup source as the HP CPU */ + ulp_lp_core_cfg_t cfg = { + .wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU, + }; + + /* Load LP core firmware */ + ESP_ERROR_CHECK(ulp_lp_core_load_binary(lp_core_main_bin_start, (lp_core_main_bin_end - lp_core_main_bin_start))); + + /* Run LP core */ + ESP_ERROR_CHECK(ulp_lp_core_run(&cfg)); + + // Give the LP core time to start up + vTaskDelay(pdMS_TO_TICKS(100)); + + printf("LP core loaded with firmware and running successfully\n"); +} + +void app_main(void) +{ + /* Load LP Core binary and start the coprocessor */ + lp_core_init(); + + for (;;) { + if (ulp_lp_timer_intr_count > 0) { + printf("Interrupt count: %"PRIu32"\n", ulp_lp_timer_intr_count); + vTaskDelay(pdMS_TO_TICKS(250)); + printf("\x1b[1F"); // Move to beginning of previous line + printf("\x1b[2K"); // Clear entire line + } + } + +} diff --git a/examples/system/ulp/lp_core/lp_timer_interrupt/pytest_lp_timer_interrupt.py b/examples/system/ulp/lp_core/lp_timer_interrupt/pytest_lp_timer_interrupt.py new file mode 100644 index 0000000000..92f07652d4 --- /dev/null +++ b/examples/system/ulp/lp_core/lp_timer_interrupt/pytest_lp_timer_interrupt.py @@ -0,0 +1,22 @@ +# SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import time + +import pytest +from pytest_embedded import Dut +from pytest_embedded_idf.utils import idf_parametrize + + +@pytest.mark.generic +@idf_parametrize('target', ['esp32c5', 'esp32c6', 'esp32p4'], indirect=['target']) +def test_lp_timer_interrupt(dut: Dut) -> None: + # Wait for LP core to be loaded and running + dut.expect_exact('LP core loaded with firmware and running successfully') + + # Add a delay to allow interrupts to occur + time.sleep(2) + + # Wait for the interrupt count line to be printed + interrupt_count = dut.expect(r'Interrupt count: (\d+)') + count = int(interrupt_count.group(1).decode('utf8')) + assert count > 0, f'Expected interrupt count to be greater than 0, got {count}' diff --git a/examples/system/ulp/lp_core/lp_timer_interrupt/sdkconfig.defaults b/examples/system/ulp/lp_core/lp_timer_interrupt/sdkconfig.defaults new file mode 100644 index 0000000000..2e829c6faa --- /dev/null +++ b/examples/system/ulp/lp_core/lp_timer_interrupt/sdkconfig.defaults @@ -0,0 +1,4 @@ +# Enable ULP +CONFIG_ULP_COPROC_ENABLED=y +CONFIG_ULP_COPROC_TYPE_LP_CORE=y +CONFIG_ULP_COPROC_RESERVE_MEM=4096