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 6be9b007b2..3664da9e9b 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 @@ -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 */ 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 00e532d0a0..c936956fca 100644 --- a/components/ulp/lp_core/lp_core/lp_core_utils.c +++ b/components/ulp/lp_core/lp_core/lp_core_utils.c @@ -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 */ } } diff --git a/components/ulp/lp_core/shared/include/ulp_lp_core_cpu_freq_shared.h b/components/ulp/lp_core/shared/include/ulp_lp_core_cpu_freq_shared.h new file mode 100644 index 0000000000..3c0970a6a7 --- /dev/null +++ b/components/ulp/lp_core/shared/include/ulp_lp_core_cpu_freq_shared.h @@ -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 diff --git a/components/ulp/test_apps/lp_core/lp_core_basic_tests/main/lp_core/test_main.c b/components/ulp/test_apps/lp_core/lp_core_basic_tests/main/lp_core/test_main.c index b9adad2670..1131cbf86a 100644 --- a/components/ulp/test_apps/lp_core/lp_core_basic_tests/main/lp_core/test_main.c +++ b/components/ulp/test_apps/lp_core/lp_core_basic_tests/main/lp_core/test_main.c @@ -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; diff --git a/components/ulp/test_apps/lp_core/lp_core_basic_tests/main/lp_core/test_shared.h b/components/ulp/test_apps/lp_core/lp_core_basic_tests/main/lp_core/test_shared.h index 8fc1c43140..d1d07d9327 100644 --- a/components/ulp/test_apps/lp_core/lp_core_basic_tests/main/lp_core/test_shared.h +++ b/components/ulp/test_apps/lp_core/lp_core_basic_tests/main/lp_core/test_shared.h @@ -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; diff --git a/components/ulp/test_apps/lp_core/lp_core_basic_tests/main/test_lp_core.c b/components/ulp/test_apps/lp_core/lp_core_basic_tests/main/test_lp_core.c index 07acfd0a67..1f29a06166 100644 --- a/components/ulp/test_apps/lp_core/lp_core_basic_tests/main/test_lp_core.c +++ b/components/ulp/test_apps/lp_core/lp_core_basic_tests/main/test_lp_core.c @@ -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) diff --git a/components/ulp/test_apps/ulp_riscv/main/CMakeLists.txt b/components/ulp/test_apps/ulp_riscv/main/CMakeLists.txt index b7b4be9ee3..98e622bc3d 100644 --- a/components/ulp/test_apps/ulp_riscv/main/CMakeLists.txt +++ b/components/ulp/test_apps/ulp_riscv/main/CMakeLists.txt @@ -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) diff --git a/components/ulp/test_apps/ulp_riscv/main/test_ulp_riscv.c b/components/ulp/test_apps/ulp_riscv/main/test_ulp_riscv.c index c8ee463678..7994118f2e 100644 --- a/components/ulp/test_apps/ulp_riscv/main/test_ulp_riscv.c +++ b/components/ulp/test_apps/ulp_riscv/main/test_ulp_riscv.c @@ -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"); +} diff --git a/components/ulp/test_apps/ulp_riscv/main/ulp/test_main.c b/components/ulp/test_apps/ulp_riscv/main/ulp/test_main.c index efed04a9b5..50a6b7e49a 100644 --- a/components/ulp/test_apps/ulp_riscv/main/ulp/test_main.c +++ b/components/ulp/test_apps/ulp_riscv/main/ulp/test_main.c @@ -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(); diff --git a/components/ulp/test_apps/ulp_riscv/main/ulp/test_main_cocpu_crash.c b/components/ulp/test_apps/ulp_riscv/main/ulp/test_main_cocpu_crash.c index 4b9e243bae..87e030e6f2 100644 --- a/components/ulp/test_apps/ulp_riscv/main/ulp/test_main_cocpu_crash.c +++ b/components/ulp/test_apps/ulp_riscv/main/ulp/test_main_cocpu_crash.c @@ -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; diff --git a/components/ulp/test_apps/ulp_riscv/main/ulp/ulp_test_shared.h b/components/ulp/test_apps/ulp_riscv/main/ulp/ulp_test_shared.h index 33ef18e67a..de72a171fd 100644 --- a/components/ulp/test_apps/ulp_riscv/main/ulp/ulp_test_shared.h +++ b/components/ulp/test_apps/ulp_riscv/main/ulp/ulp_test_shared.h @@ -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; diff --git a/components/ulp/ulp_riscv/shared/include/ulp_riscv_cpu_freq_shared.h b/components/ulp/ulp_riscv/shared/include/ulp_riscv_cpu_freq_shared.h new file mode 100644 index 0000000000..21ab59fd08 --- /dev/null +++ b/components/ulp/ulp_riscv/shared/include/ulp_riscv_cpu_freq_shared.h @@ -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 diff --git a/components/ulp/ulp_riscv/ulp_core/include/ulp_riscv_utils.h b/components/ulp/ulp_riscv/ulp_core/include/ulp_riscv_utils.h index a7d6838d55..958680e374 100644 --- a/components/ulp/ulp_riscv/ulp_core/include/ulp_riscv_utils.h +++ b/components/ulp/ulp_riscv/ulp_core/include/ulp_riscv_utils.h @@ -14,6 +14,7 @@ extern "C" { #include #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 * diff --git a/components/ulp/ulp_riscv/ulp_core/ulp_riscv_uart.c b/components/ulp/ulp_riscv/ulp_core/ulp_riscv_uart.c index b9d127f66e..14567b7c21 100644 --- a/components/ulp/ulp_riscv/ulp_core/ulp_riscv_uart.c +++ b/components/ulp/ulp_riscv/ulp_core/ulp_riscv_uart.c @@ -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) { diff --git a/components/ulp/ulp_riscv/ulp_core/ulp_riscv_utils.c b/components/ulp/ulp_riscv/ulp_core/ulp_riscv_utils.c index 8fce93363a..9bbe5feeae 100644 --- a/components/ulp/ulp_riscv/ulp_core/ulp_riscv_utils.c +++ b/components/ulp/ulp_riscv/ulp_core/ulp_riscv_utils.c @@ -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 +} diff --git a/examples/system/ulp/ulp_riscv/ds18b20_onewire/main/ulp/main.c b/examples/system/ulp/ulp_riscv/ds18b20_onewire/main/ulp/main.c index 462dbce430..7c55e7b0d8 100644 --- a/examples/system/ulp/ulp_riscv/ds18b20_onewire/main/ulp/main.c +++ b/examples/system/ulp/ulp_riscv/ds18b20_onewire/main/ulp/main.c @@ -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; } diff --git a/examples/system/ulp/ulp_riscv/gpio_interrupt/main/ulp/main.c b/examples/system/ulp/ulp_riscv/gpio_interrupt/main/ulp/main.c index a63a01561d..18efcf6195 100644 --- a/examples/system/ulp/ulp_riscv/gpio_interrupt/main/ulp/main.c +++ b/examples/system/ulp/ulp_riscv/gpio_interrupt/main/ulp/main.c @@ -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 */ diff --git a/examples/system/ulp/ulp_riscv/i2c/main/ulp/main.c b/examples/system/ulp/ulp_riscv/i2c/main/ulp/main.c index 0f598956c4..e9e456210c 100644 --- a/examples/system/ulp/ulp_riscv/i2c/main/ulp/main.c +++ b/examples/system/ulp/ulp_riscv/i2c/main/ulp/main.c @@ -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 */ diff --git a/examples/system/ulp/ulp_riscv/interrupts/main/ulp/main.c b/examples/system/ulp/ulp_riscv/interrupts/main/ulp/main.c index 09731c85c2..24da216f7c 100644 --- a/examples/system/ulp/ulp_riscv/interrupts/main/ulp/main.c +++ b/examples/system/ulp/ulp_riscv/interrupts/main/ulp/main.c @@ -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; diff --git a/examples/system/ulp/ulp_riscv/uart_print/main/ulp/main.c b/examples/system/ulp/ulp_riscv/uart_print/main/ulp/main.c index 4ea27a781e..9bcc7a6891 100644 --- a/examples/system/ulp/ulp_riscv/uart_print/main/ulp/main.c +++ b/examples/system/ulp/ulp_riscv/uart_print/main/ulp/main.c @@ -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); } }