Merge branch 'contrib/github_pr_15717' into 'master'

feat(ulp): LP Timer interrupt support (GitHub PR)

Closes IDFGH-15026

See merge request espressif/esp-idf!38613
This commit is contained in:
Sudeep Mohanty
2025-04-25 16:07:31 +08:00
15 changed files with 222 additions and 1 deletions

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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

View File

@@ -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
*

View File

@@ -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");

View File

@@ -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)

View File

@@ -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)

View File

@@ -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
```

View File

@@ -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}")

View File

@@ -0,0 +1,33 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdint.h>
#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;
}

View File

@@ -0,0 +1,51 @@
/*
* SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <stdio.h>
#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
}
}
}

View File

@@ -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}'

View File

@@ -0,0 +1,4 @@
# Enable ULP
CONFIG_ULP_COPROC_ENABLED=y
CONFIG_ULP_COPROC_TYPE_LP_CORE=y
CONFIG_ULP_COPROC_RESERVE_MEM=4096