feat(ulp): Improved ULP delay API accuracy and removed floating point operations

Closes https://github.com/espressif/esp-idf/issues/17494
Closes https://github.com/espressif/esp-idf/issues/16891
This commit is contained in:
Konstantin Kondrashov
2026-03-20 16:44:14 +08:00
committed by BOT
parent 329ed7ddce
commit 6fe2cf575f
20 changed files with 680 additions and 73 deletions
@@ -53,14 +53,50 @@ static inline uint32_t ulp_lp_core_get_cpu_cycles(void)
}
/**
* @brief Makes the co-processor busy wait for a certain number of microseconds
* @brief Check whether an mcycle-based timeout has elapsed.
*
* @note A timeout value of -1 means "wait forever".
* Other values are interpreted as unsigned cycle counts.
*
* @param start_cycle_count Cycle counter value captured at timeout start.
* @param cycles_to_wait Timeout in CPU cycles, or -1 to disable timeout.
*
* @return true if timeout elapsed, false otherwise.
*/
static inline bool ulp_lp_core_is_timeout_elapsed(uint32_t start_cycle_count, int32_t cycles_to_wait)
{
if (cycles_to_wait == -1) {
return false;
}
return (ulp_lp_core_get_cpu_cycles() - start_cycle_count) >= (uint32_t)cycles_to_wait;
}
/**
* @brief Makes the co-processor busy-wait for a certain number of microseconds.
*
* @note The maximum supported delay depends on the LP core clock source and frequency.
* For values above the limits below, the computed delay may overflow and the result
* is undefined.
* - LP core @ 16 MHz (RC_FAST / default): us must be <= 134217727 (about 134.2 s)
* - LP core @ 40 MHz (XTAL 40 MHz): us must be <= 53687091 (about 53.7 s)
* - LP core @ 48 MHz (XTAL 48 MHz): us must be <= 44739242 (about 44.7 s)
*
* @param us Number of microseconds to busy-wait for
*/
void ulp_lp_core_delay_us(uint32_t us);
/**
* @brief Makes the co-processor busy wait for a certain number of cycles
* @brief Makes the co-processor busy-wait for a certain number of cycles.
*
* @note The maximum supported delay is 0x7FFFFFFF cycles.
* For larger values, the behavior is undefined. Split longer delays into smaller
* chunks if needed.
*
* For reference, this corresponds approximately to:
* - LP core @ 16 MHz (RC_FAST / default): 0x7FFFFFFF cycles ≈ 134.2 s
* - LP core @ 40 MHz (XTAL 40 MHz): 0x7FFFFFFF cycles ≈ 53.7 s
* - LP core @ 48 MHz (XTAL 48 MHz): 0x7FFFFFFF cycles ≈ 44.7 s
*
* @param cycles Number of cycles to busy-wait for
*/
+14 -21
View File
@@ -24,19 +24,7 @@
#endif
#include "esp_cpu.h"
/* LP_FAST_CLK is not very accurate, for now use a rough estimate */
#if CONFIG_RTC_FAST_CLK_SRC_RC_FAST
#define LP_CORE_CPU_FREQUENCY_HZ 16000000 // For P4 TRM says 20 MHz by default, but we tune it closer to 16 MHz
#elif CONFIG_RTC_FAST_CLK_SRC_XTAL
#if SOC_XTAL_SUPPORT_48M
#define LP_CORE_CPU_FREQUENCY_HZ 48000000
#else
#define LP_CORE_CPU_FREQUENCY_HZ 40000000
#endif
#else // Default value in chip without rtc fast clock sel option
#define LP_CORE_CPU_FREQUENCY_HZ 16000000
#endif
#include "ulp_lp_core_cpu_freq_shared.h"
static uint32_t lp_wakeup_cause = 0;
@@ -117,11 +105,14 @@ void ulp_lp_core_wakeup_main_processor(void)
*/
void ulp_lp_core_delay_us(uint32_t us)
{
uint32_t start = RV_READ_CSR(mcycle);
uint32_t end = us * (LP_CORE_CPU_FREQUENCY_HZ / 1000000);
if (us == 0) {
return;
}
uint32_t start = RV_READ_CSR(mcycle) - ULP_LP_CORE_DELAY_CALL_OVERHEAD_IN_CYCLES;
uint32_t req_delay = us * LP_CORE_CYCLES_PER_US_NUM / LP_CORE_CYCLES_PER_US_DENOM;
while ((RV_READ_CSR(mcycle) - start) < end) {
/* nothing to do */
while ((uint32_t)(RV_READ_CSR(mcycle) - start) < req_delay) {
/* busy wait */
}
}
@@ -132,11 +123,13 @@ void ulp_lp_core_delay_us(uint32_t us)
*/
void ulp_lp_core_delay_cycles(uint32_t cycles)
{
uint32_t start = RV_READ_CSR(mcycle);
uint32_t end = cycles;
if (cycles <= ULP_LP_CORE_DELAY_CALL_OVERHEAD_IN_CYCLES) {
return;
}
while ((RV_READ_CSR(mcycle) - start) < end) {
/* nothing to do */
uint32_t start = RV_READ_CSR(mcycle) - ULP_LP_CORE_DELAY_CALL_OVERHEAD_IN_CYCLES;
while ((uint32_t)(RV_READ_CSR(mcycle) - start) < cycles) {
/* busy wait */
}
}
@@ -0,0 +1,41 @@
/*
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#pragma once
#include "sdkconfig.h"
#include "soc/soc_caps.h"
/* LP_FAST_CLK is not very accurate, for now use a rough estimate */
#if CONFIG_RTC_FAST_CLK_SRC_RC_FAST
#define LP_CORE_CPU_FREQUENCY_HZ 16000000U /* For P4 TRM says 20 MHz by default, but we tune it closer to 16 MHz */
#define LP_CORE_CYCLES_PER_US_NUM 16U
#define LP_CORE_CYCLES_PER_US_DENOM 1U
#elif CONFIG_RTC_FAST_CLK_SRC_XTAL
#if SOC_XTAL_SUPPORT_48M
#define LP_CORE_CPU_FREQUENCY_HZ 48000000U
#define LP_CORE_CYCLES_PER_US_NUM 48U
#define LP_CORE_CYCLES_PER_US_DENOM 1U
#else
#define LP_CORE_CPU_FREQUENCY_HZ 40000000U
#define LP_CORE_CYCLES_PER_US_NUM 40U
#define LP_CORE_CYCLES_PER_US_DENOM 1U
#endif
#else // Default value in chip without rtc fast clock sel option
#define LP_CORE_CPU_FREQUENCY_HZ 16000000U
#define LP_CORE_CYCLES_PER_US_NUM 16U
#define LP_CORE_CYCLES_PER_US_DENOM 1U
#endif
/**
* @brief Compensation for LP core delay function overhead in CPU cycles.
*
* @details Estimated cycles consumed by delay loop function call and measurement
* overhead. Derived from empirical "LP core delay calibration" test measurements.
* Used by delay functions to improve accuracy for short durations.
*
* @note Value is calibration-specific and may vary with compiler optimization.
*/
#define ULP_LP_CORE_DELAY_CALL_OVERHEAD_IN_CYCLES 11U