mirror of
https://github.com/espressif/esp-idf.git
synced 2026-04-27 19:13:21 +00:00
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:
committed by
BOT
parent
329ed7ddce
commit
6fe2cf575f
@@ -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
|
||||
*/
|
||||
|
||||
@@ -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
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -15,9 +15,49 @@ volatile lp_core_test_command_reply_t main_cpu_reply = LP_CORE_COMMAND_INVALID;
|
||||
volatile lp_core_test_commands_t command_resp = LP_CORE_NO_COMMAND;
|
||||
volatile uint32_t test_data_in = 0;
|
||||
volatile uint32_t test_data_out = 0;
|
||||
volatile uint32_t delay_start_cycles = 0;
|
||||
volatile uint32_t delay_end_cycles = 0;
|
||||
volatile uint32_t delay_sub_command = LP_CORE_DELAY_SUBCMD_NONE;
|
||||
|
||||
volatile uint32_t incrementer = 0;
|
||||
|
||||
static void run_delay_calibration_test(lp_core_test_commands_t calibration_cmd, bool use_cycles_delay)
|
||||
{
|
||||
register uint32_t start, end, overhead = 0;
|
||||
const uint32_t iterations = 5;
|
||||
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
start = ulp_lp_core_get_cpu_cycles();
|
||||
end = ulp_lp_core_get_cpu_cycles();
|
||||
overhead += end - start;
|
||||
}
|
||||
overhead /= iterations;
|
||||
|
||||
command_resp = calibration_cmd;
|
||||
|
||||
while (main_cpu_command == calibration_cmd) {
|
||||
delay_sub_command = LP_CORE_DELAY_SUBCMD_READY;
|
||||
while (delay_sub_command != LP_CORE_DELAY_SUBCMD_RUN) { };
|
||||
register uint32_t requested_delay = test_data_in;
|
||||
ulp_lp_core_delay_us(1);
|
||||
|
||||
if (use_cycles_delay) {
|
||||
start = ulp_lp_core_get_cpu_cycles();
|
||||
ulp_lp_core_delay_cycles(requested_delay);
|
||||
end = ulp_lp_core_get_cpu_cycles();
|
||||
} else {
|
||||
start = ulp_lp_core_get_cpu_cycles();
|
||||
ulp_lp_core_delay_us(requested_delay);
|
||||
end = ulp_lp_core_get_cpu_cycles();
|
||||
}
|
||||
|
||||
delay_sub_command = LP_CORE_DELAY_SUBCMD_NONE;
|
||||
|
||||
delay_start_cycles = start;
|
||||
delay_end_cycles = end - overhead;
|
||||
}
|
||||
}
|
||||
|
||||
void handle_commands(lp_core_test_commands_t cmd)
|
||||
{
|
||||
|
||||
@@ -44,6 +84,14 @@ void handle_commands(lp_core_test_commands_t cmd)
|
||||
main_cpu_command = LP_CORE_NO_COMMAND;
|
||||
break;
|
||||
|
||||
case LP_CORE_DELAY_CYCLES_CALIBRATION_TEST:
|
||||
run_delay_calibration_test(LP_CORE_DELAY_CYCLES_CALIBRATION_TEST, true);
|
||||
break;
|
||||
|
||||
case LP_CORE_DELAY_US_CALIBRATION_TEST:
|
||||
run_delay_calibration_test(LP_CORE_DELAY_US_CALIBRATION_TEST, false);
|
||||
break;
|
||||
|
||||
case LP_CORE_DEEP_SLEEP_WAKEUP_SHORT_DELAY_TEST:
|
||||
/* Echo the command ID back to the main CPU */
|
||||
command_resp = LP_CORE_DEEP_SLEEP_WAKEUP_SHORT_DELAY_TEST;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -18,6 +18,8 @@
|
||||
typedef enum {
|
||||
LP_CORE_READ_WRITE_TEST = 1,
|
||||
LP_CORE_DELAY_TEST,
|
||||
LP_CORE_DELAY_CYCLES_CALIBRATION_TEST,
|
||||
LP_CORE_DELAY_US_CALIBRATION_TEST,
|
||||
LP_CORE_DEEP_SLEEP_WAKEUP_SHORT_DELAY_TEST,
|
||||
LP_CORE_DEEP_SLEEP_WAKEUP_LONG_DELAY_TEST,
|
||||
LP_CORE_LP_UART_WRITE_TEST,
|
||||
@@ -33,3 +35,9 @@ typedef enum {
|
||||
LP_CORE_COMMAND_NOK,
|
||||
LP_CORE_COMMAND_INVALID,
|
||||
} lp_core_test_command_reply_t;
|
||||
|
||||
typedef enum {
|
||||
LP_CORE_DELAY_SUBCMD_NONE = 0,
|
||||
LP_CORE_DELAY_SUBCMD_READY,
|
||||
LP_CORE_DELAY_SUBCMD_RUN,
|
||||
} lp_core_delay_sub_command_t;
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
#include "lp_core_test_app_wake_stub.h"
|
||||
#endif
|
||||
#include "lp_core_test_app_isr.h"
|
||||
#include "ulp_lp_core_cpu_freq_shared.h"
|
||||
|
||||
#if SOC_RTC_TIMER_V2_SUPPORTED
|
||||
#include "lp_core_test_app_set_timer_wakeup.h"
|
||||
@@ -145,6 +146,127 @@ TEST_CASE("Test LP core delay", "[lp_core]")
|
||||
clear_test_cmds();
|
||||
}
|
||||
|
||||
static void print_table_header(void)
|
||||
{
|
||||
printf("%10s %10s %10s %10s %10s\n",
|
||||
"----------", "----------", "----------", "----------", "----------");
|
||||
printf("%10s %10s %10s %10s %10s\n",
|
||||
"delay(us)", "expec_tk", "avg_tk", "error_tk", "in_range");
|
||||
printf("%10s %10s %10s %10s %10s\n",
|
||||
"----------", "----------", "----------", "----------", "----------");
|
||||
}
|
||||
|
||||
static void run_delay_calibration_test(lp_core_test_commands_t delay_calibration_cmd, const char *delay_api_name)
|
||||
{
|
||||
volatile uint32_t *command_resp = (volatile uint32_t *)&ulp_command_resp;
|
||||
volatile uint32_t *main_cpu_command = (volatile uint32_t *)&ulp_main_cpu_command;
|
||||
volatile uint32_t *delay_sub_command = (volatile uint32_t *)&ulp_delay_sub_command;
|
||||
volatile uint32_t *test_data_in = (volatile uint32_t *)&ulp_test_data_in;
|
||||
const uint32_t test_delays_us[] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
|
||||
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
|
||||
30, 40, 50, 70, 90,
|
||||
100, 200, 500, 1000, 2000, 5000,
|
||||
10000, 20000, 50000, 100000,
|
||||
};
|
||||
const uint32_t lp_cpu_freq_hz = LP_CORE_CPU_FREQUENCY_HZ;
|
||||
const double lp_tick_time_us = 1000000.0 / lp_cpu_freq_hz;
|
||||
const double lp_ticks_per_us = 1.0 / lp_tick_time_us;
|
||||
const double error_limit_time_us = 0.30;
|
||||
const int32_t error_limit_ticks = (int32_t)(error_limit_time_us * lp_ticks_per_us + 0.5);
|
||||
const uint32_t warmup_exclude_ticks = 0;
|
||||
int32_t correction_ticks_checked = 0;
|
||||
uint32_t out_of_range_count = 0;
|
||||
uint32_t checked_measurements = 0;
|
||||
uint32_t skipped_warmup_measurements = 0;
|
||||
printf("Delay API under test: %s\n", delay_api_name);
|
||||
printf("LP CPU frequency: %" PRIu32 " Hz, tick time: %.6f us, ticks/us: %.3f\n",
|
||||
lp_cpu_freq_hz, lp_tick_time_us, lp_ticks_per_us);
|
||||
printf("Tolerance: +/- %" PRId32 " ticks (%.3f us)\n", error_limit_ticks, error_limit_time_us);
|
||||
|
||||
/* Load ULP firmware and start the coprocessor */
|
||||
ulp_lp_core_cfg_t cfg = {
|
||||
.wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU,
|
||||
};
|
||||
|
||||
load_and_start_lp_core_firmware(&cfg, lp_core_main_bin_start, lp_core_main_bin_end);
|
||||
|
||||
*delay_sub_command = LP_CORE_DELAY_SUBCMD_NONE;
|
||||
*command_resp = LP_CORE_NO_COMMAND;
|
||||
*main_cpu_command = delay_calibration_cmd;
|
||||
|
||||
while (*command_resp != delay_calibration_cmd) { };
|
||||
|
||||
print_table_header();
|
||||
const uint32_t num_delays = sizeof(test_delays_us) / sizeof(test_delays_us[0]);
|
||||
for (uint32_t delay_idx = 0; delay_idx < num_delays; delay_idx++) {
|
||||
const uint32_t requested_delay_us = test_delays_us[delay_idx];
|
||||
uint64_t sum_delay_ticks = 0;
|
||||
const uint32_t samples = 3;
|
||||
const uint32_t expected_ticks = requested_delay_us * lp_ticks_per_us;
|
||||
|
||||
for (uint32_t sample_idx = 0; sample_idx < samples; sample_idx++) {
|
||||
|
||||
*test_data_in = (delay_calibration_cmd == LP_CORE_DELAY_CYCLES_CALIBRATION_TEST) ? expected_ticks : requested_delay_us;
|
||||
while (*delay_sub_command != LP_CORE_DELAY_SUBCMD_READY) { };
|
||||
*delay_sub_command = LP_CORE_DELAY_SUBCMD_RUN;
|
||||
|
||||
/* Wait for the ULP to finish its delay before reading the cycle counters from RTC memory.
|
||||
* The HP core delay must be at least as long as the requested ULP delay, with some margin
|
||||
* to ensure that we do not read RTC memory while the ULP is running the delay calibration code. */
|
||||
vTaskDelay(((requested_delay_us / 1000 + portTICK_PERIOD_MS + 1) / portTICK_PERIOD_MS) * 3);
|
||||
sum_delay_ticks += ulp_delay_end_cycles - ulp_delay_start_cycles;
|
||||
}
|
||||
|
||||
const uint64_t avg_delay_ticks = sum_delay_ticks / samples;
|
||||
const int32_t error_tk = expected_ticks - avg_delay_ticks;
|
||||
const char *range_status = "PASS";
|
||||
|
||||
if (expected_ticks <= warmup_exclude_ticks) {
|
||||
skipped_warmup_measurements++;
|
||||
range_status = "SKIP";
|
||||
} else {
|
||||
checked_measurements++;
|
||||
correction_ticks_checked += error_tk;
|
||||
if ((error_tk < -error_limit_ticks) || (error_tk > error_limit_ticks)) {
|
||||
out_of_range_count++;
|
||||
range_status = "FAIL";
|
||||
}
|
||||
}
|
||||
|
||||
printf("%10" PRIu32 " %10" PRIu32 " %10" PRIu64 " %10" PRId32 " %10s\n",
|
||||
requested_delay_us, expected_ticks, avg_delay_ticks, error_tk, range_status);
|
||||
}
|
||||
print_table_header();
|
||||
|
||||
/* Release LP core from delay-test loop */
|
||||
*main_cpu_command = LP_CORE_NO_COMMAND;
|
||||
ulp_test_data_in = 0;
|
||||
*delay_sub_command = LP_CORE_DELAY_SUBCMD_RUN;
|
||||
|
||||
TEST_ASSERT_MESSAGE(checked_measurements > 0, "No delay calibration points were checked");
|
||||
double total_correct_ticks = (double) correction_ticks_checked / (double) checked_measurements;
|
||||
printf("Average correction ticks: %.1f (%.3f us)\n", total_correct_ticks, total_correct_ticks * lp_tick_time_us);
|
||||
printf("Range-check summary (%s): checked=%" PRIu32 ", skipped_warmup=%" PRIu32 ", out_of_range=%" PRIu32
|
||||
", limit=+/- %" PRId32 " ticks\n",
|
||||
delay_api_name, checked_measurements, skipped_warmup_measurements, out_of_range_count, error_limit_ticks);
|
||||
TEST_ASSERT_EQUAL_UINT32_MESSAGE(0, out_of_range_count, "Delay calibration has out-of-range error_tk values");
|
||||
TEST_ASSERT_DOUBLE_WITHIN(5.0, 0.0, total_correct_ticks);
|
||||
|
||||
clear_test_cmds();
|
||||
}
|
||||
|
||||
TEST_CASE("Test delay calibration for ulp_lp_core_delay_cycles", "[lp_core]")
|
||||
{
|
||||
run_delay_calibration_test(LP_CORE_DELAY_CYCLES_CALIBRATION_TEST, "ulp_lp_core_delay_cycles");
|
||||
}
|
||||
|
||||
TEST_CASE("Test delay calibration for ulp_lp_core_delay_us", "[lp_core]")
|
||||
{
|
||||
run_delay_calibration_test(LP_CORE_DELAY_US_CALIBRATION_TEST, "ulp_lp_core_delay_us");
|
||||
}
|
||||
|
||||
#define LP_TIMER_TEST_DURATION_S (5)
|
||||
#define LP_TIMER_TEST_SLEEP_DURATION_US (20000)
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ set(ulp_sources4 "ulp/test_main_i2c.c")
|
||||
|
||||
idf_component_register(SRCS ${app_sources}
|
||||
INCLUDE_DIRS "ulp"
|
||||
REQUIRES ulp unity test_utils driver
|
||||
REQUIRES ulp unity test_utils driver esp_timer
|
||||
WHOLE_ARCHIVE)
|
||||
|
||||
set(ulp_app_name ulp_test_app)
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2010-2025 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2010-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -23,6 +23,8 @@
|
||||
#include "freertos/task.h"
|
||||
#include "freertos/semphr.h"
|
||||
#include "esp_adc/adc_oneshot.h"
|
||||
#include "ulp_riscv_cpu_freq_shared.h"
|
||||
#include "esp_timer.h"
|
||||
|
||||
#define ULP_WAKEUP_PERIOD 1000000 // 1 second
|
||||
|
||||
@@ -468,3 +470,163 @@ TEST_CASE("ULP ADC can init-deinit-init", "[ulp]")
|
||||
TEST_ASSERT_EQUAL(ESP_OK, ulp_adc_init(&riscv_adc_cfg));
|
||||
TEST_ASSERT_EQUAL(ESP_OK, ulp_adc_deinit());
|
||||
}
|
||||
|
||||
TEST_CASE("Test ULP RISCV delay", "[ulp]")
|
||||
{
|
||||
int64_t start, diff;
|
||||
const uint32_t delay_period_us = 5000000;
|
||||
const uint32_t delta_us = 500000; // RTC FAST is not very accurate
|
||||
|
||||
volatile uint32_t *command_resp = (volatile uint32_t *)&ulp_command_resp;
|
||||
volatile uint32_t *main_cpu_command = (volatile uint32_t *)&ulp_main_cpu_command;
|
||||
volatile uint32_t *main_cpu_reply = (volatile uint32_t *)&ulp_main_cpu_reply;
|
||||
volatile uint32_t *test_data_in = (volatile uint32_t *)&ulp_riscv_test_data_in;
|
||||
|
||||
/* Load ULP RISC-V firmware and start the coprocessor */
|
||||
load_and_start_ulp_firmware(ulp_main_bin_start, ulp_main_bin_length);
|
||||
|
||||
/* Setup test data */
|
||||
*command_resp = RISCV_NO_COMMAND;
|
||||
*main_cpu_reply = RISCV_NO_COMMAND;
|
||||
*test_data_in = delay_period_us;
|
||||
*main_cpu_command = RISCV_DELAY_TEST;
|
||||
|
||||
/* Wait till we receive the correct command response */
|
||||
while (*command_resp != RISCV_DELAY_TEST) {
|
||||
}
|
||||
|
||||
start = esp_timer_get_time();
|
||||
|
||||
/* Wait till we receive COMMAND_OK reply */
|
||||
while (*main_cpu_reply != RISCV_COMMAND_OK) {
|
||||
}
|
||||
|
||||
diff = esp_timer_get_time() - start;
|
||||
|
||||
printf("Waited for %" PRIi64 "us, expected: %" PRIu32 "us\n", diff, delay_period_us);
|
||||
TEST_ASSERT_INT_WITHIN(delta_us, delay_period_us, diff);
|
||||
|
||||
/* Clear test data */
|
||||
*main_cpu_command = RISCV_NO_COMMAND;
|
||||
}
|
||||
|
||||
static void print_delay_table_header(void)
|
||||
{
|
||||
printf("%10s %10s %10s %10s %10s\n",
|
||||
"----------", "----------", "----------", "----------", "----------");
|
||||
printf("%10s %10s %10s %10s %10s\n",
|
||||
"delay(us)", "expec_tk", "avg_tk", "error_tk", "in_range");
|
||||
printf("%10s %10s %10s %10s %10s\n",
|
||||
"----------", "----------", "----------", "----------", "----------");
|
||||
}
|
||||
|
||||
static void run_delay_calibration_test(riscv_test_commands_t delay_calibration_cmd, const char *delay_api_name)
|
||||
{
|
||||
volatile uint32_t *command_resp = (volatile uint32_t *)&ulp_command_resp;
|
||||
volatile uint32_t *main_cpu_command = (volatile uint32_t *)&ulp_main_cpu_command;
|
||||
volatile uint32_t *delay_sub_command = (volatile uint32_t *)&ulp_delay_sub_command;
|
||||
volatile uint32_t *test_data_in = (volatile uint32_t *)&ulp_riscv_test_data_in;
|
||||
const uint32_t test_delays_us[] = {
|
||||
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
|
||||
10, 11, 12, 13, 14, 15, 16, 17, 18, 19,
|
||||
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
|
||||
30, 40, 50, 70, 90,
|
||||
100, 200, 500, 1000, 2000, 5000,
|
||||
10000, 20000, 50000, 100000,
|
||||
};
|
||||
const uint32_t ulp_cpu_freq_hz = ULP_RISCV_CPU_FREQUENCY_HZ;
|
||||
const double ulp_tick_time_us = 1000000.0 / ulp_cpu_freq_hz;
|
||||
const double ulp_ticks_per_us = 1.0 / ulp_tick_time_us;
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
const uint32_t warmup_exclude_ticks = 25;
|
||||
const double error_limit_time_us = 2.0;
|
||||
#else // ESP32S3
|
||||
const uint32_t warmup_exclude_ticks = 17;
|
||||
const double error_limit_time_us = 1.0;
|
||||
#endif
|
||||
const int32_t error_limit_ticks = (int32_t)(error_limit_time_us * ulp_ticks_per_us + 0.5);
|
||||
int32_t correction_ticks_checked = 0;
|
||||
uint32_t out_of_range_count = 0;
|
||||
uint32_t checked_measurements = 0;
|
||||
uint32_t skipped_warmup_measurements = 0;
|
||||
printf("Delay API under test: %s\n", delay_api_name);
|
||||
printf("ULP CPU frequency: %" PRIu32 " Hz, tick time: %.6f us, ticks/us: %.3f\n",
|
||||
ulp_cpu_freq_hz, ulp_tick_time_us, ulp_ticks_per_us);
|
||||
printf("Tolerance: +/- %" PRId32 " ticks (%.3f us)\n", error_limit_ticks, error_limit_time_us);
|
||||
printf("Skipping delay calibration points with expected ticks <= %" PRIu32 " ticks (%.3f us) as warmup\n",
|
||||
warmup_exclude_ticks, warmup_exclude_ticks * ulp_tick_time_us);
|
||||
|
||||
load_and_start_ulp_firmware(ulp_main_bin_start, ulp_main_bin_length);
|
||||
|
||||
*delay_sub_command = RISCV_DELAY_SUBCMD_NONE;
|
||||
*command_resp = RISCV_NO_COMMAND;
|
||||
*main_cpu_command = delay_calibration_cmd;
|
||||
|
||||
while (*command_resp != delay_calibration_cmd) { };
|
||||
|
||||
print_delay_table_header();
|
||||
const uint32_t num_delays = sizeof(test_delays_us) / sizeof(test_delays_us[0]);
|
||||
for (uint32_t delay_idx = 0; delay_idx < num_delays; delay_idx++) {
|
||||
const uint32_t requested_delay_us = test_delays_us[delay_idx];
|
||||
uint64_t sum_delay_ticks = 0;
|
||||
const uint32_t samples = 3;
|
||||
const uint32_t expected_ticks = requested_delay_us * ulp_ticks_per_us;
|
||||
|
||||
for (uint32_t sample_idx = 0; sample_idx < samples; sample_idx++) {
|
||||
|
||||
*test_data_in = (delay_calibration_cmd == RISCV_DELAY_CYCLES_CALIBRATION_TEST) ? expected_ticks : requested_delay_us;
|
||||
while (*delay_sub_command != RISCV_DELAY_SUBCMD_READY) { };
|
||||
*delay_sub_command = RISCV_DELAY_SUBCMD_RUN;
|
||||
|
||||
/* Wait for the ULP to finish its delay before reading the cycle counters from RTC memory.
|
||||
* The HP core delay must be at least as long as the requested ULP delay, with some margin
|
||||
* to ensure that we do not read RTC memory while the ULP is running the delay calibration code. */
|
||||
vTaskDelay(((requested_delay_us / 1000 + portTICK_PERIOD_MS + 1) / portTICK_PERIOD_MS) * 3);
|
||||
sum_delay_ticks += ulp_delay_end_cycles - ulp_delay_start_cycles;
|
||||
}
|
||||
|
||||
const uint64_t avg_delay_ticks = sum_delay_ticks / samples;
|
||||
const int32_t error_tk = expected_ticks - avg_delay_ticks;
|
||||
const char *range_status = "PASS";
|
||||
|
||||
if (expected_ticks <= warmup_exclude_ticks) {
|
||||
skipped_warmup_measurements++;
|
||||
range_status = "SKIP";
|
||||
} else {
|
||||
checked_measurements++;
|
||||
correction_ticks_checked += error_tk;
|
||||
if ((error_tk < -error_limit_ticks) || (error_tk > error_limit_ticks)) {
|
||||
out_of_range_count++;
|
||||
range_status = "FAIL";
|
||||
}
|
||||
}
|
||||
|
||||
printf("%10" PRIu32 " %10" PRIu32 " %10" PRIu64 " %10" PRId32 " %10s\n",
|
||||
requested_delay_us, expected_ticks, avg_delay_ticks, error_tk, range_status);
|
||||
}
|
||||
print_delay_table_header();
|
||||
|
||||
// Release the ULP from the delay test loop
|
||||
*main_cpu_command = RISCV_NO_COMMAND;
|
||||
*test_data_in = 0;
|
||||
*delay_sub_command = RISCV_DELAY_SUBCMD_RUN;
|
||||
|
||||
TEST_ASSERT_MESSAGE(checked_measurements > 0, "No delay calibration points were checked");
|
||||
double total_correct_ticks = (double)correction_ticks_checked / (double)checked_measurements;
|
||||
printf("Average correction ticks: %.1f (%.3f us)\n", total_correct_ticks, total_correct_ticks * ulp_tick_time_us);
|
||||
printf("Range-check summary (%s): checked=%" PRIu32 ", skipped_warmup=%" PRIu32 ", out_of_range=%" PRIu32
|
||||
", limit=+/- %" PRId32 " ticks\n",
|
||||
delay_api_name, checked_measurements, skipped_warmup_measurements, out_of_range_count, error_limit_ticks);
|
||||
TEST_ASSERT_EQUAL_UINT32_MESSAGE(0, out_of_range_count, "Delay calibration has out-of-range error_tk values");
|
||||
TEST_ASSERT_DOUBLE_WITHIN(7.0, 0.0, total_correct_ticks);
|
||||
}
|
||||
|
||||
TEST_CASE("Test delay calibration for ulp_riscv_delay_cycles", "[ulp]")
|
||||
{
|
||||
run_delay_calibration_test(RISCV_DELAY_CYCLES_CALIBRATION_TEST, "ulp_riscv_delay_cycles");
|
||||
}
|
||||
|
||||
TEST_CASE("Test delay calibration for ulp_riscv_delay_us", "[ulp]")
|
||||
{
|
||||
run_delay_calibration_test(RISCV_DELAY_US_CALIBRATION_TEST, "ulp_riscv_delay_us");
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2010-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2010-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -18,10 +18,51 @@ volatile riscv_test_commands_t command_resp = RISCV_NO_COMMAND;
|
||||
volatile uint32_t riscv_test_data_in = 0;
|
||||
volatile uint32_t riscv_test_data_out = 0;
|
||||
volatile uint32_t riscv_counter = 0;
|
||||
volatile uint32_t delay_start_cycles = 0;
|
||||
volatile uint32_t delay_end_cycles = 0;
|
||||
volatile uint32_t delay_sub_command = RISCV_DELAY_SUBCMD_NONE;
|
||||
|
||||
volatile uint32_t riscv_incrementer = 0;
|
||||
ulp_riscv_lock_t lock;
|
||||
|
||||
static void run_delay_calibration_test(riscv_test_commands_t calibration_cmd, bool use_cycles_delay)
|
||||
{
|
||||
register uint32_t start, end, overhead = 0;
|
||||
const uint32_t iterations = 5;
|
||||
|
||||
for (int i = 0; i < iterations; i++) {
|
||||
start = ulp_riscv_get_cpu_cycles();
|
||||
end = ulp_riscv_get_cpu_cycles();
|
||||
overhead += end - start;
|
||||
}
|
||||
overhead /= iterations;
|
||||
|
||||
/* Enter delay-test session and stay here until HP signals completion. */
|
||||
command_resp = calibration_cmd;
|
||||
|
||||
while (main_cpu_command == calibration_cmd) {
|
||||
delay_sub_command = RISCV_DELAY_SUBCMD_READY;
|
||||
while (delay_sub_command != RISCV_DELAY_SUBCMD_RUN) { };
|
||||
register uint32_t requested_delay = riscv_test_data_in;
|
||||
ulp_riscv_delay_us(1);
|
||||
|
||||
if (use_cycles_delay) {
|
||||
start = ulp_riscv_get_cpu_cycles();
|
||||
ulp_riscv_delay_cycles(requested_delay);
|
||||
end = ulp_riscv_get_cpu_cycles();
|
||||
} else {
|
||||
start = ulp_riscv_get_cpu_cycles();
|
||||
ulp_riscv_delay_us(requested_delay);
|
||||
end = ulp_riscv_get_cpu_cycles();
|
||||
}
|
||||
|
||||
delay_sub_command = RISCV_DELAY_SUBCMD_NONE;
|
||||
|
||||
delay_start_cycles = start;
|
||||
delay_end_cycles = end - overhead;
|
||||
}
|
||||
}
|
||||
|
||||
void handle_commands(riscv_test_commands_t cmd)
|
||||
{
|
||||
riscv_counter++;
|
||||
@@ -41,6 +82,25 @@ void handle_commands(riscv_test_commands_t cmd)
|
||||
ulp_riscv_wakeup_main_processor();
|
||||
break;
|
||||
|
||||
case RISCV_DELAY_TEST:
|
||||
/* Echo the command ID back to the main CPU */
|
||||
command_resp = RISCV_DELAY_TEST;
|
||||
|
||||
ulp_riscv_delay_us(riscv_test_data_in);
|
||||
main_cpu_reply = RISCV_COMMAND_OK;
|
||||
main_cpu_command = RISCV_NO_COMMAND;
|
||||
/* Wakeup the main CPU */
|
||||
ulp_riscv_wakeup_main_processor();
|
||||
break;
|
||||
|
||||
case RISCV_DELAY_CYCLES_CALIBRATION_TEST:
|
||||
run_delay_calibration_test(RISCV_DELAY_CYCLES_CALIBRATION_TEST, true);
|
||||
break;
|
||||
|
||||
case RISCV_DELAY_US_CALIBRATION_TEST:
|
||||
run_delay_calibration_test(RISCV_DELAY_US_CALIBRATION_TEST, false);
|
||||
break;
|
||||
|
||||
case RISCV_DEEP_SLEEP_WAKEUP_SHORT_DELAY_TEST:
|
||||
/* Echo the command ID back to the main CPU */
|
||||
command_resp = RISCV_DEEP_SLEEP_WAKEUP_SHORT_DELAY_TEST;
|
||||
@@ -48,7 +108,7 @@ void handle_commands(riscv_test_commands_t cmd)
|
||||
/* Set the command reply status */
|
||||
main_cpu_reply = RISCV_COMMAND_OK;
|
||||
|
||||
ulp_riscv_delay_cycles(1000 * ULP_RISCV_CYCLES_PER_MS);
|
||||
ulp_riscv_delay_us(1000000);
|
||||
|
||||
/* Wakeup the main CPU */
|
||||
ulp_riscv_wakeup_main_processor();
|
||||
@@ -61,7 +121,7 @@ void handle_commands(riscv_test_commands_t cmd)
|
||||
/* Set the command reply status */
|
||||
main_cpu_reply = RISCV_COMMAND_OK;
|
||||
|
||||
ulp_riscv_delay_cycles(10000 * ULP_RISCV_CYCLES_PER_MS);
|
||||
ulp_riscv_delay_us(10000000);
|
||||
|
||||
/* Wakeup the main CPU */
|
||||
ulp_riscv_wakeup_main_processor();
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
int main(void)
|
||||
{
|
||||
// Wait for the main core in the test case to enter lightsleep
|
||||
ulp_riscv_delay_cycles(100 * ULP_RISCV_CYCLES_PER_MS);
|
||||
ulp_riscv_delay_us(100000);
|
||||
/* Make sure ULP core crashes by doing a NULL pointer access */
|
||||
uint32_t *null_ptr = NULL;
|
||||
*null_ptr = 1;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -16,6 +16,9 @@
|
||||
|
||||
typedef enum {
|
||||
RISCV_READ_WRITE_TEST = 1,
|
||||
RISCV_DELAY_TEST,
|
||||
RISCV_DELAY_CYCLES_CALIBRATION_TEST,
|
||||
RISCV_DELAY_US_CALIBRATION_TEST,
|
||||
RISCV_DEEP_SLEEP_WAKEUP_SHORT_DELAY_TEST,
|
||||
RISCV_DEEP_SLEEP_WAKEUP_LONG_DELAY_TEST,
|
||||
RISCV_LIGHT_SLEEP_WAKEUP_TEST,
|
||||
@@ -29,3 +32,9 @@ typedef enum {
|
||||
RISCV_COMMAND_NOK,
|
||||
RISCV_COMMAND_INVALID,
|
||||
} riscv_test_command_reply_t;
|
||||
|
||||
typedef enum {
|
||||
RISCV_DELAY_SUBCMD_NONE = 0,
|
||||
RISCV_DELAY_SUBCMD_READY,
|
||||
RISCV_DELAY_SUBCMD_RUN,
|
||||
} riscv_delay_sub_command_t;
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "sdkconfig.h"
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
// ULP-RISC-V runs at 8.5MHz, which corresponds to 17/2 = 8.5 cycles per microsecond.
|
||||
#define ULP_RISCV_CPU_FREQUENCY_HZ 8500000U
|
||||
#define ULP_RISCV_CYCLES_PER_US_NUM 17U
|
||||
#define ULP_RISCV_CYCLES_PER_US_DENOM 2U
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
// ULP-RISC-V runs at 17.5MHz, which corresponds to 35/2 = 17.5 cycles per microsecond.
|
||||
#define ULP_RISCV_CPU_FREQUENCY_HZ 17500000U
|
||||
#define ULP_RISCV_CYCLES_PER_US_NUM 35U
|
||||
#define ULP_RISCV_CYCLES_PER_US_DENOM 2U
|
||||
#endif
|
||||
@@ -14,6 +14,7 @@ extern "C" {
|
||||
#include <stdint.h>
|
||||
#include "ulp_riscv_register_ops.h"
|
||||
#include "ulp_riscv_interrupt.h"
|
||||
#include "ulp_riscv_cpu_freq_shared.h"
|
||||
|
||||
/**
|
||||
* @brief Wakeup main CPU from sleep or deep sleep.
|
||||
@@ -79,15 +80,8 @@ void ulp_riscv_timer_resume(void);
|
||||
asm volatile("rdcycle %0;" : "=r"(__ccount)); \
|
||||
__ccount; })
|
||||
|
||||
#if CONFIG_IDF_TARGET_ESP32S2
|
||||
/* These are only approximate default numbers, the default frequency
|
||||
of the 8M oscillator is 8.5MHz +/- 5%, at the default DCAP setting
|
||||
*/
|
||||
#define ULP_RISCV_CYCLES_PER_US 8.5
|
||||
#elif CONFIG_IDF_TARGET_ESP32S3
|
||||
#define ULP_RISCV_CYCLES_PER_US 17.5
|
||||
#endif
|
||||
#define ULP_RISCV_CYCLES_PER_MS ULP_RISCV_CYCLES_PER_US*1000
|
||||
#define ULP_RISCV_CYCLES_PER_US ULP_RISCV_CYCLES_PER_US_NUM / ULP_RISCV_CYCLES_PER_US_DENOM
|
||||
#define ULP_RISCV_CYCLES_PER_MS 1000U * ULP_RISCV_CYCLES_PER_US
|
||||
|
||||
/**
|
||||
* @brief Retrieves the current number of CPU cycles.
|
||||
@@ -100,25 +94,70 @@ static inline uint32_t ulp_riscv_get_cpu_cycles(void)
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Makes the co-processor busy wait for a certain number of cycles
|
||||
* @brief Check whether an mcycle-based timeout has elapsed.
|
||||
*
|
||||
* @note This function is not accurate for delays shorter than 20 cycles,
|
||||
* as the function's own overhead may exceed the requested delay.
|
||||
* @note A timeout value of -1 means "wait forever".
|
||||
* Other values are interpreted as unsigned cycle counts.
|
||||
*
|
||||
* @param cycles Number of cycles to busy wait
|
||||
* @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.
|
||||
*/
|
||||
void static inline ulp_riscv_delay_cycles(uint32_t cycles)
|
||||
static inline bool ulp_riscv_is_timeout_elapsed(uint32_t start_cycle_count, int32_t cycles_to_wait)
|
||||
{
|
||||
if (cycles <= 20) { // the estimate of cycles for this function
|
||||
if (cycles_to_wait == -1) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (ulp_riscv_get_cpu_cycles() - start_cycle_count) >= (uint32_t)cycles_to_wait;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Makes the co-processor busy-wait for a certain number of CPU cycles.
|
||||
*
|
||||
* @note This function is not accurate for delays shorter than 20 cycles because the
|
||||
* function overhead may exceed the requested delay.
|
||||
*
|
||||
* @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:
|
||||
* - ESP32-S2 ULP-RISC-V @ 8.5 MHz: 0x7FFFFFFF cycles ≈ 252.645 s
|
||||
* - ESP32-S3 ULP-RISC-V @ 17.5 MHz: 0x7FFFFFFF cycles ≈ 122.713 s
|
||||
*
|
||||
* @param cycles Number of cycles to busy-wait.
|
||||
*/
|
||||
static inline void ulp_riscv_delay_cycles(uint32_t cycles)
|
||||
{
|
||||
if (cycles <= 20U) { // estimate of cycles for this function overhead
|
||||
return;
|
||||
}
|
||||
/* Off with the estimate of cycles to improve accuracy */
|
||||
uint32_t end = ULP_RISCV_GET_CCOUNT() + cycles - 20;
|
||||
while (ULP_RISCV_GET_CCOUNT() < end) {
|
||||
/* Wait */
|
||||
// To improve accuracy subtract (20 + 15) cycles overhead, defined by delay calibration test
|
||||
uint32_t start = ULP_RISCV_GET_CCOUNT() - 20U - 15U;
|
||||
while ((uint32_t)(ULP_RISCV_GET_CCOUNT() - start) < cycles) {
|
||||
/* busy wait */
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Makes the co-processor busy-wait for a certain number of microseconds.
|
||||
*
|
||||
* @note This function is not accurate for short delays because the function overhead
|
||||
* may exceed the requested delay. For very small delays the implementation uses
|
||||
* a fixed sequence of NOPs (chip-dependent thresholds).
|
||||
*
|
||||
* @note The maximum supported delay depends on the ULP-RISC-V cycle counter width and on
|
||||
* the internal cycles-per-microsecond conversion. For values above the limits below,
|
||||
* the computed delay may overflow and the result is undefined.
|
||||
* - ESP32-S2 ULP-RISC-V @ 8.5 MHz: delay_us must be <= 252645135 (about 252.6 s)
|
||||
* - ESP32-S3 ULP-RISC-V @ 17.5 MHz: delay_us must be <= 122713351 (about 122.7 s)
|
||||
*
|
||||
* @param delay_us Number of microseconds to busy wait.
|
||||
*/
|
||||
void ulp_riscv_delay_us(uint32_t delay_us);
|
||||
|
||||
/**
|
||||
* @brief Clears the GPIO wakeup interrupt bit
|
||||
*
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
#include "ulp_riscv_uart_ulp_core.h"
|
||||
|
||||
/* We calculate the bit duration at compile time to speed up and avoid pulling in soft-float libs */
|
||||
#define BIT_DURATION_CYCLES ( ULP_RISCV_CYCLES_PER_US * ((1000*1000) / CONFIG_ULP_RISCV_UART_BAUDRATE) )
|
||||
#define BIT_DURATION_CYCLES ( (ULP_RISCV_CYCLES_PER_US_NUM * 1000000) / (ULP_RISCV_CYCLES_PER_US_DENOM * CONFIG_ULP_RISCV_UART_BAUDRATE) )
|
||||
|
||||
void ulp_riscv_uart_init(ulp_riscv_uart_t *uart, const ulp_riscv_uart_cfg_t *cfg)
|
||||
{
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2015-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
@@ -11,6 +11,7 @@
|
||||
#include "soc/rtc_cntl_reg.h"
|
||||
#include "soc/soc_ulp.h"
|
||||
#include "soc/sens_reg.h"
|
||||
#include "ulp_riscv_cpu_freq_shared.h"
|
||||
|
||||
void ulp_riscv_rescue_from_monitor(void)
|
||||
{
|
||||
@@ -77,3 +78,71 @@ void ulp_riscv_trigger_sw_intr(void)
|
||||
}
|
||||
|
||||
#endif /* CONFIG_ULP_RISCV_INTERRUPT_ENABLE */
|
||||
|
||||
void ulp_riscv_delay_us(uint32_t delay_us)
|
||||
{
|
||||
#if CONFIG_IDF_TARGET_ESP32S3
|
||||
/*
|
||||
* For very small delays, entering the generic cycle-count loop adds too much fixed overhead.
|
||||
* Use a short calibrated NOP path instead to improve small-delay accuracy on ESP32-S3.
|
||||
*/
|
||||
if (delay_us <= 5) {
|
||||
goto fast_return;
|
||||
}
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
/*
|
||||
* Same principle as S3, but with a different threshold due to target-specific timing/overhead.
|
||||
*/
|
||||
if (delay_us <= 10) {
|
||||
goto fast_return;
|
||||
}
|
||||
#endif
|
||||
|
||||
/*
|
||||
* Generic delay path:
|
||||
* - Convert requested microseconds to CPU cycles using ratio macros.
|
||||
* - Pre-subtract measured function overhead (55 cycles), so observed delay is closer to request.
|
||||
*/
|
||||
uint32_t start = ulp_riscv_get_cpu_cycles() - 55U;
|
||||
uint32_t req_delay = delay_us * ULP_RISCV_CYCLES_PER_US_NUM / ULP_RISCV_CYCLES_PER_US_DENOM;
|
||||
|
||||
/*
|
||||
* Busy-wait until elapsed cycles reach req_delay.
|
||||
* uint32_t subtraction intentionally relies on wrap-around-safe arithmetic for cycle counter rollover.
|
||||
*/
|
||||
while ((uint32_t)(ulp_riscv_get_cpu_cycles() - start) < req_delay) {
|
||||
/* busy wait */
|
||||
}
|
||||
return;
|
||||
|
||||
fast_return:
|
||||
#if CONFIG_IDF_TARGET_ESP32S3
|
||||
/*
|
||||
* Fast path for tiny delays:
|
||||
* Use discrete NOP counts calibrated for this target.
|
||||
* Note: (delay_us == 0 || delay_us <= 2) is intentionally kept as-is to avoid behavior changes.
|
||||
*/
|
||||
if (delay_us == 0 || delay_us <= 2) {
|
||||
return;
|
||||
} else if (delay_us <= 3) {
|
||||
asm volatile("nop\n");
|
||||
} else if (delay_us <= 4) {
|
||||
asm volatile("nop\n nop\n");
|
||||
} else {
|
||||
asm volatile("nop\n nop\n nop\n nop\n nop\n");
|
||||
}
|
||||
#elif CONFIG_IDF_TARGET_ESP32S2
|
||||
/*
|
||||
* ESP32-S2 calibrated NOP mapping for very short delays.
|
||||
*/
|
||||
if (delay_us == 0) {
|
||||
return;
|
||||
} else if (delay_us <= 6) {
|
||||
asm volatile("nop\n");
|
||||
} else if (delay_us <= 8) {
|
||||
asm volatile("nop\n nop\n");
|
||||
} else {
|
||||
asm volatile("nop\n nop\n nop\n nop\n nop\n nop\n");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2021-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2021-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -43,7 +43,7 @@ static void ds18b20_write_bit(bool bit)
|
||||
}
|
||||
|
||||
/* Write slot duration at least 60 us */
|
||||
ulp_riscv_delay_cycles(60 * ULP_RISCV_CYCLES_PER_US);
|
||||
ulp_riscv_delay_us(60);
|
||||
ulp_riscv_gpio_output_level(EXAMPLE_1WIRE_GPIO, 1);
|
||||
}
|
||||
|
||||
@@ -56,11 +56,11 @@ static bool ds18b20_read_bit(void)
|
||||
ulp_riscv_gpio_output_level(EXAMPLE_1WIRE_GPIO, 1);
|
||||
|
||||
/* Must sample within 15 us of the failing edge */
|
||||
ulp_riscv_delay_cycles(5 * ULP_RISCV_CYCLES_PER_US);
|
||||
ulp_riscv_delay_us(5);
|
||||
bit = ulp_riscv_gpio_get_level(EXAMPLE_1WIRE_GPIO);
|
||||
|
||||
/* Read slot duration at least 60 us */
|
||||
ulp_riscv_delay_cycles(55 * ULP_RISCV_CYCLES_PER_US);
|
||||
ulp_riscv_delay_us(55);
|
||||
|
||||
return bit;
|
||||
}
|
||||
@@ -86,15 +86,15 @@ bool ds18b20_reset_pulse(void)
|
||||
bool presence_pulse;
|
||||
/* min 480 us reset pulse + 480 us reply time is specified by datasheet */
|
||||
ulp_riscv_gpio_output_level(EXAMPLE_1WIRE_GPIO, 0);
|
||||
ulp_riscv_delay_cycles(480 * ULP_RISCV_CYCLES_PER_US);
|
||||
ulp_riscv_delay_us(480);
|
||||
|
||||
ulp_riscv_gpio_output_level(EXAMPLE_1WIRE_GPIO, 1);
|
||||
|
||||
/* Wait for ds18b20 to pull low before sampling */
|
||||
ulp_riscv_delay_cycles(60 * ULP_RISCV_CYCLES_PER_US);
|
||||
ulp_riscv_delay_us(60);
|
||||
presence_pulse = ulp_riscv_gpio_get_level(EXAMPLE_1WIRE_GPIO) == 0;
|
||||
|
||||
ulp_riscv_delay_cycles(420 * ULP_RISCV_CYCLES_PER_US);
|
||||
ulp_riscv_delay_us(420);
|
||||
|
||||
return presence_pulse;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -23,7 +23,7 @@ int main (void)
|
||||
|
||||
/* Wakeup interrupt is a level interrupt, wait 1 sec to
|
||||
allow user to release button to avoid waking up the ULP multiple times */
|
||||
ulp_riscv_delay_cycles(1000*1000 * ULP_RISCV_CYCLES_PER_US);
|
||||
ulp_riscv_delay_us(1000000);
|
||||
ulp_riscv_gpio_wakeup_clear();
|
||||
|
||||
/* ulp_riscv_halt() is called automatically when main exits */
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -71,7 +71,7 @@ static void bmp180_read_ut_data(int16_t *ut_data)
|
||||
ulp_riscv_i2c_master_write_to_device(&cmd, 1);
|
||||
|
||||
/* Wait at least 4.5 milliseconds for the sensor to complete the reading */
|
||||
ulp_riscv_delay_cycles(5 * ULP_RISCV_CYCLES_PER_MS);
|
||||
ulp_riscv_delay_us(5000);
|
||||
|
||||
/* Read uncompensated temperature data */
|
||||
bmp180_read16((uint16_t *)ut_data, BMP180_SENSOR_REG_ADDR_SENSOR_DATA_MSB, BMP180_SENSOR_REG_ADDR_SENSOR_DATA_LSB);
|
||||
@@ -92,26 +92,26 @@ static void bmp180_read_up_data(int32_t *up_data, oss_mode_t oss_mode)
|
||||
{
|
||||
case OSS_0:
|
||||
cmd = BMP180_SENSOR_CMD_READ_PRESSURE_OSS_0;
|
||||
wait = 5; // Wait atleast 4.5 msec
|
||||
wait = 5; // Wait at least 4.5 msec
|
||||
break;
|
||||
case OSS_1:
|
||||
cmd = BMP180_SENSOR_CMD_READ_PRESSURE_OSS_1;
|
||||
wait = 8; // Wait atleast 7.5 msec
|
||||
wait = 8; // Wait at least 7.5 msec
|
||||
break;
|
||||
case OSS_2:
|
||||
cmd = BMP180_SENSOR_CMD_READ_PRESSURE_OSS_2;
|
||||
wait = 14; // Wait atleast 13.5 msec
|
||||
wait = 14; // Wait at least 13.5 msec
|
||||
break;
|
||||
case OSS_3:
|
||||
cmd = BMP180_SENSOR_CMD_READ_PRESSURE_OSS_3;
|
||||
wait = 26; // Wait atleast 25.5 msec
|
||||
wait = 26; // Wait at least 25.5 msec
|
||||
break;
|
||||
}
|
||||
|
||||
ulp_riscv_i2c_master_write_to_device(&cmd, 1);
|
||||
|
||||
/* Wait for the required amount of time for the sensor to complete the reading */
|
||||
ulp_riscv_delay_cycles(wait * ULP_RISCV_CYCLES_PER_MS);
|
||||
ulp_riscv_delay_us(wait * 1000);
|
||||
|
||||
/* Read uncompensated temperature data */
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2023-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -56,7 +56,7 @@ int main(void) {
|
||||
ulp_riscv_wakeup_main_processor();
|
||||
}
|
||||
|
||||
ulp_riscv_delay_cycles(1000 * ULP_RISCV_CYCLES_PER_MS);
|
||||
ulp_riscv_delay_us(1000000);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
||||
* SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
*
|
||||
* SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
*/
|
||||
@@ -41,6 +41,6 @@ int main (void)
|
||||
ulp_riscv_print_str("\n");
|
||||
|
||||
cnt++;
|
||||
ulp_riscv_delay_cycles(1000 * ULP_RISCV_CYCLES_PER_MS);
|
||||
ulp_riscv_delay_us(1000000);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user