From a3f167f1c4a40908d54f8849e19fc8c95f4e540e Mon Sep 17 00:00:00 2001 From: Meet Patel Date: Wed, 4 Feb 2026 12:08:28 +0530 Subject: [PATCH 1/4] refactor(ulp): Allow both ULP-FSM and ULP-RISCV to enable at build time Updated kconfig option type and other supporting changes in build system to allow enabling both ULP FSM and ULP RISCV simultaneously. Users can choose at run time which one to initialize and use. NOTE: Both ULP FSM and ULP RISCV can't be used simultaneously at run time because they share some common hardware like RTC slow memory space. Closes https://github.com/espressif/esp-idf/issues/12999 --- components/esp_hw_support/sleep_modes.c | 23 ++-- components/ulp/CMakeLists.txt | 3 +- components/ulp/Kconfig | 13 ++- components/ulp/cmake/IDFULPProject.cmake | 104 +++++++++++------- components/ulp/project_include.cmake | 69 ++++++++++-- .../ulp/ulp_fsm/ulp_adc/main/CMakeLists.txt | 2 +- 6 files changed, 154 insertions(+), 60 deletions(-) diff --git a/components/esp_hw_support/sleep_modes.c b/components/esp_hw_support/sleep_modes.c index c350649201..1b3b2e277f 100644 --- a/components/esp_hw_support/sleep_modes.c +++ b/components/esp_hw_support/sleep_modes.c @@ -1058,19 +1058,23 @@ static esp_err_t SLEEP_FN_ATTR esp_sleep_start(uint32_t sleep_flags, uint32_t cl // Enable ULP wakeup #if CONFIG_ULP_COPROC_TYPE_FSM if (s_config.wakeup_triggers & RTC_ULP_TRIG_EN) { -#elif CONFIG_ULP_COPROC_TYPE_RISCV - if (s_config.wakeup_triggers & (RTC_COCPU_TRIG_EN | RTC_COCPU_TRAP_TRIG_EN)) { -#elif CONFIG_ULP_COPROC_TYPE_LP_CORE - if (s_config.wakeup_triggers & (RTC_LP_CORE_TRIG_EN | RTC_LP_CORE_TRAP_TRIG_EN)) { -#endif #ifdef CONFIG_IDF_TARGET_ESP32 rtc_hal_ulp_wakeup_enable(); -#elif CONFIG_ULP_COPROC_TYPE_LP_CORE - pmu_ll_hp_clear_sw_intr_status(&PMU); #else rtc_hal_ulp_int_clear(); #endif } +#endif +#if CONFIG_ULP_COPROC_TYPE_RISCV + if (s_config.wakeup_triggers & (RTC_COCPU_TRIG_EN | RTC_COCPU_TRAP_TRIG_EN)) { + rtc_hal_ulp_int_clear(); + } +#endif +#if CONFIG_ULP_COPROC_TYPE_LP_CORE + if (s_config.wakeup_triggers & (RTC_LP_CORE_TRIG_EN | RTC_LP_CORE_TRAP_TRIG_EN)) { + pmu_ll_hp_clear_sw_intr_status(&PMU); + } +#endif #endif // CONFIG_ULP_COPROC_ENABLED misc_modules_sleep_prepare(sleep_flags, deep_sleep); @@ -1800,7 +1804,10 @@ esp_err_t esp_sleep_enable_ulp_wakeup(void) } #endif //CONFIG_IDF_TARGET_ESP32 -#if CONFIG_ULP_COPROC_TYPE_FSM +#if CONFIG_ULP_COPROC_TYPE_FSM && CONFIG_ULP_COPROC_TYPE_RISCV + s_config.wakeup_triggers |= (RTC_ULP_TRIG_EN | RTC_COCPU_TRIG_EN | RTC_COCPU_TRAP_TRIG_EN); + return ESP_OK; +#elif CONFIG_ULP_COPROC_TYPE_FSM s_config.wakeup_triggers |= RTC_ULP_TRIG_EN; return ESP_OK; #elif CONFIG_ULP_COPROC_TYPE_RISCV diff --git a/components/ulp/CMakeLists.txt b/components/ulp/CMakeLists.txt index 405ddcc362..c0ca9be67c 100644 --- a/components/ulp/CMakeLists.txt +++ b/components/ulp/CMakeLists.txt @@ -40,8 +40,9 @@ if(CONFIG_ULP_COPROC_TYPE_FSM OR CONFIG_ULP_COPROC_TYPE_RISCV) list(APPEND srcs "ulp_fsm/ulp.c" "ulp_fsm/ulp_macro.c") + endif() - elseif(CONFIG_ULP_COPROC_TYPE_RISCV) + if(CONFIG_ULP_COPROC_TYPE_RISCV) list(APPEND srcs "ulp_riscv/ulp_riscv.c" "ulp_riscv/ulp_riscv_lock.c" diff --git a/components/ulp/Kconfig b/components/ulp/Kconfig index 2fbb436130..6eca1766e6 100644 --- a/components/ulp/Kconfig +++ b/components/ulp/Kconfig @@ -8,23 +8,24 @@ menu "Ultra Low Power (ULP) Co-processor" Enable this feature if you plan to use the ULP Co-processor. Once this option is enabled, further ULP co-processor configuration will appear in the menu. - choice ULP_COPROC_TYPE - prompt "ULP Co-processor type" + menu "ULP Coprocessor types" depends on ULP_COPROC_ENABLED - default ULP_COPROC_TYPE_RISCV if (IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3) - help - Choose the ULP Coprocessor type: ULP FSM (Finite State Machine) or ULP RISC-V. config ULP_COPROC_TYPE_FSM bool "ULP FSM (Finite State Machine)" depends on SOC_ULP_FSM_SUPPORTED + default y if IDF_TARGET_ESP32 + config ULP_COPROC_TYPE_RISCV bool "ULP RISC-V" depends on SOC_RISCV_COPROC_SUPPORTED + default y if (IDF_TARGET_ESP32S2 || IDF_TARGET_ESP32S3) && !ULP_COPROC_TYPE_FSM + config ULP_COPROC_TYPE_LP_CORE bool "LP core RISC-V" depends on SOC_LP_CORE_SUPPORTED - endchoice + default y if (!IDF_TARGET_ESP32 && !IDF_TARGET_ESP32S2 && !IDF_TARGET_ESP32S3) + endmenu config ULP_COPROC_RESERVE_MEM int diff --git a/components/ulp/cmake/IDFULPProject.cmake b/components/ulp/cmake/IDFULPProject.cmake index ec5d85e9db..aa0ccb4f07 100644 --- a/components/ulp/cmake/IDFULPProject.cmake +++ b/components/ulp/cmake/IDFULPProject.cmake @@ -2,24 +2,46 @@ include(${SDKCONFIG_CMAKE}) enable_language(C ASM) set(CMAKE_EXECUTABLE_SUFFIX ".elf") +# Logic to determine ULP type and set reusable flags +set(BUILD_RISCV OFF) +set(BUILD_FSM OFF) +set(BUILD_LP_CORE OFF) + +# If ULP_TYPE is explicitly set, use it; otherwise fall back to CONFIG checks +if(ULP_TYPE) + string(TOLOWER "${ULP_TYPE}" ulp_type_lower) + if(ulp_type_lower STREQUAL "riscv") + set(BUILD_RISCV ON) + elseif(ulp_type_lower STREQUAL "fsm") + set(BUILD_FSM ON) + elseif(ulp_type_lower STREQUAL "lp_core") + set(BUILD_LP_CORE ON) + endif() +elseif(CONFIG_ULP_COPROC_TYPE_RISCV) + set(BUILD_RISCV ON) +elseif(CONFIG_ULP_COPROC_TYPE_LP_CORE) + set(BUILD_LP_CORE ON) +elseif(CONFIG_ULP_COPROC_TYPE_FSM) + set(BUILD_FSM ON) +endif() + # Check the supported assembler version -if(CONFIG_ULP_COPROC_TYPE_FSM) +if(BUILD_FSM) check_expected_tool_version("esp32ulp-elf" ${CMAKE_ASM_COMPILER}) endif() function(ulp_apply_default_options ulp_app_name) - if(CONFIG_ULP_COPROC_TYPE_RISCV) + if(BUILD_RISCV) target_link_options(${ulp_app_name} PRIVATE "-nostartfiles") target_link_options(${ulp_app_name} PRIVATE -Wl,--gc-sections) target_link_options(${ulp_app_name} PRIVATE "-Wl,--no-warn-rwx-segments") target_link_options(${ulp_app_name} PRIVATE -Wl,-Map=${CMAKE_CURRENT_BINARY_DIR}/${ulp_app_name}.map) - - elseif(CONFIG_ULP_COPROC_TYPE_LP_CORE) + elseif(BUILD_LP_CORE) target_link_options(${ulp_app_name} PRIVATE "-nostartfiles") target_link_options(${ulp_app_name} PRIVATE "-Wl,--no-warn-rwx-segments") target_link_options(${ulp_app_name} PRIVATE -Wl,--gc-sections) target_link_options(${ulp_app_name} PRIVATE -Wl,-Map=${CMAKE_CURRENT_BINARY_DIR}/${ulp_app_name}.map) - else() + elseif(BUILD_FSM) target_link_options(${ulp_app_name} PRIVATE -Map=${CMAKE_CURRENT_BINARY_DIR}/${ulp_app_name}.map) endif() endfunction() @@ -52,12 +74,14 @@ function(ulp_apply_default_sources ulp_app_name) target_include_directories(${ulp_app_name} PRIVATE ${COMPONENT_INCLUDES} ${sdkconfig_dir}) # Pre-process the linker script - if(CONFIG_ULP_COPROC_TYPE_RISCV) + if(BUILD_RISCV) set(ULP_LD_TEMPLATE ${IDF_PATH}/components/ulp/ld/ulp_riscv.ld) - elseif(CONFIG_ULP_COPROC_TYPE_LP_CORE) + elseif(BUILD_LP_CORE) set(ULP_LD_TEMPLATE ${IDF_PATH}/components/ulp/ld/lp_core_riscv.ld) - else() + elseif(BUILD_FSM) set(ULP_LD_TEMPLATE ${IDF_PATH}/components/ulp/ld/ulp_fsm.ld) + else() + message(FATAL_ERROR "Unable to determine ULP type. ") endif() get_filename_component(ULP_LD_SCRIPT ${ULP_LD_TEMPLATE} NAME) @@ -81,8 +105,15 @@ function(ulp_apply_default_sources ulp_app_name) # To avoid warning "Manually-specified variables were not used by the project" set(bypassWarning "${IDF_TARGET}") set(bypassWarning "${ULP_VAR_PREFIX}") + set(bypassWarning "${ULP_TYPE}") - if(CONFIG_ULP_COPROC_TYPE_RISCV) + # Save user sources before adding core sources + set(ULP_USER_SOURCES ${ULP_S_SOURCES}) + + # Clear ULP_S_SOURCES and rebuild it with only the sources we need + set(ULP_S_SOURCES ${ULP_USER_SOURCES}) + + if(BUILD_RISCV) #risc-v ulp uses extra files for building: list(APPEND ULP_S_SOURCES "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_vectors.S" @@ -97,7 +128,6 @@ function(ulp_apply_default_sources ulp_app_name) "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_gpio.c" "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_interrupt.c") - target_sources(${ulp_app_name} PRIVATE ${ULP_S_SOURCES}) #Makes the csr utillies for riscv visible: target_include_directories(${ulp_app_name} PRIVATE "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/include" @@ -107,7 +137,32 @@ function(ulp_apply_default_sources ulp_app_name) target_compile_definitions(${ulp_app_name} PRIVATE IS_ULP_COCPU) target_compile_definitions(${ulp_app_name} PRIVATE ULP_RISCV_REGISTER_OPS) - elseif(CONFIG_ULP_COPROC_TYPE_LP_CORE) + elseif(BUILD_FSM) + foreach(ulp_s_source ${ULP_S_SOURCES}) + get_filename_component(ulp_ps_source ${ulp_s_source} NAME_WE) + set(ulp_ps_output ${CMAKE_CURRENT_BINARY_DIR}/${ulp_ps_source}.ulp.S) + # Put all arguments to the list + set(preprocessor_args -D__ASSEMBLER__ -E -P -xc -o ${ulp_ps_output} ${ULP_PREPRO_ARGS} ${ulp_s_source}) + + set(compiler_arguments_file ${CMAKE_CURRENT_BINARY_DIR}/${ulp_ps_source}_args.txt) + create_arg_file("${preprocessor_args}" "${compiler_arguments_file}") + + # Generate preprocessed assembly files. + add_custom_command(OUTPUT ${ulp_ps_output} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + COMMAND ${CMAKE_C_COMPILER} @${compiler_arguments_file} + DEPENDS ${ulp_s_source} + VERBATIM) + # During assembly file compilation, output listing files as well. + set_source_files_properties(${ulp_ps_output} + PROPERTIES COMPILE_FLAGS + "-al=${CMAKE_CURRENT_BINARY_DIR}/${ulp_ps_source}.lst") + list(APPEND ULP_PS_SOURCES ${ulp_ps_output}) + endforeach() + + target_sources(${ulp_app_name} PRIVATE ${ULP_PS_SOURCES}) + + elseif(BUILD_LP_CORE) list(APPEND ULP_S_SOURCES "${IDF_PATH}/components/ulp/lp_core/lp_core/start.S" "${IDF_PATH}/components/ulp/lp_core/lp_core/vector.S" @@ -167,31 +222,6 @@ function(ulp_apply_default_sources ulp_app_name) "${IDF_PATH}/components/ulp/lp_core/shared/include") target_compile_definitions(${ulp_app_name} PRIVATE IS_ULP_COCPU) - else() - foreach(ulp_s_source ${ULP_S_SOURCES}) - get_filename_component(ulp_ps_source ${ulp_s_source} NAME_WE) - set(ulp_ps_output ${CMAKE_CURRENT_BINARY_DIR}/${ulp_ps_source}.ulp.S) - # Put all arguments to the list - set(preprocessor_args -D__ASSEMBLER__ -E -P -xc -o ${ulp_ps_output} ${ULP_PREPRO_ARGS} ${ulp_s_source}) - - set(compiler_arguments_file ${CMAKE_CURRENT_BINARY_DIR}/${ulp_ps_source}_args.txt) - create_arg_file("${preprocessor_args}" "${compiler_arguments_file}") - - # Generate preprocessed assembly files. - add_custom_command(OUTPUT ${ulp_ps_output} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - COMMAND ${CMAKE_C_COMPILER} @${compiler_arguments_file} - DEPENDS ${ulp_s_source} - VERBATIM) - # During assembly file compilation, output listing files as well. - set_source_files_properties(${ulp_ps_output} - PROPERTIES COMPILE_FLAGS - "-al=${CMAKE_CURRENT_BINARY_DIR}/${ulp_ps_source}.lst") - list(APPEND ULP_PS_SOURCES ${ulp_ps_output}) - endforeach() - - target_sources(${ulp_app_name} PRIVATE ${ULP_PS_SOURCES}) - endif() endfunction() @@ -206,7 +236,7 @@ function(ulp_add_build_binary_targets ulp_app_name) target_compile_options(${ulp_app_name} PRIVATE $<$:-specs=picolibcpp.specs>) endif() - if(CONFIG_ULP_COPROC_TYPE_LP_CORE) + if(BUILD_LP_CORE) set(ULP_BASE_ADDR "0x0") else() set(ULP_BASE_ADDR "0x50000000") diff --git a/components/ulp/project_include.cmake b/components/ulp/project_include.cmake index 05550d9f42..891bbaecc4 100644 --- a/components/ulp/project_include.cmake +++ b/components/ulp/project_include.cmake @@ -2,7 +2,7 @@ # # Create ULP binary and embed into the application. -function(__setup_ulp_project app_name project_path prefix s_sources exp_dep_srcs) +function(__setup_ulp_project app_name project_path prefix type s_sources exp_dep_srcs) if(NOT CMAKE_BUILD_EARLY_EXPANSION) spaces2list(s_sources) @@ -39,13 +39,36 @@ function(__setup_ulp_project app_name project_path prefix s_sources exp_dep_srcs idf_build_get_property(extra_cmake_args EXTRA_CMAKE_ARGS) if(IDF_TARGET STREQUAL "esp32") + # esp32 only supports FSM + if(type) + string(TOLOWER "${type}" type_lower) + if(type_lower STREQUAL "riscv") + message(FATAL_ERROR "TYPE=riscv is not supported on ${IDF_TARGET}. " + "Only FSM type is available for ULP on this target.") + endif() + endif() set(TOOLCHAIN_FLAG ${idf_path}/components/ulp/cmake/toolchain-${idf_target}-ulp.cmake) set(ULP_IS_RISCV OFF) elseif(IDF_TARGET STREQUAL "esp32s2" OR IDF_TARGET STREQUAL "esp32s3") - if(CONFIG_ULP_COPROC_TYPE_RISCV STREQUAL "y") - set(TOOLCHAIN_FLAG ${idf_path}/components/ulp/cmake/toolchain-ulp-riscv.cmake) + # If both FSM and RISC-V are enabled in sdkconfig and a TYPE was + # provided by the caller, use TYPE to disambiguate which toolchain + # to select for this ULP external project. + if((CONFIG_ULP_COPROC_TYPE_RISCV STREQUAL "y") AND (CONFIG_ULP_COPROC_TYPE_FSM STREQUAL "y")) + message(STATUS "Both RISCV and FSM are enabled, using '${type}' toolchain for ${app_name} ULP project.") + string(TOLOWER "${type}" type_lower) + if(type_lower STREQUAL "riscv") + set(TOOLCHAIN_FLAG ${idf_path}/components/ulp/cmake/toolchain-ulp-riscv.cmake) + elseif(type_lower STREQUAL "fsm") + set(TOOLCHAIN_FLAG ${idf_path}/components/ulp/cmake/toolchain-${idf_target}-ulp.cmake) + else() + message(FATAL_ERROR "Invalid ULP_TYPE '${type}'; expected 'fsm' or 'riscv'.") + endif() else() - set(TOOLCHAIN_FLAG ${idf_path}/components/ulp/cmake/toolchain-${idf_target}-ulp.cmake) + if(CONFIG_ULP_COPROC_TYPE_RISCV STREQUAL "y") + set(TOOLCHAIN_FLAG ${idf_path}/components/ulp/cmake/toolchain-ulp-riscv.cmake) + else() + set(TOOLCHAIN_FLAG ${idf_path}/components/ulp/cmake/toolchain-${idf_target}-ulp.cmake) + endif() endif() elseif(CONFIG_ULP_COPROC_TYPE_LP_CORE) set(TOOLCHAIN_FLAG ${idf_path}/components/ulp/cmake/toolchain-lp-core-riscv.cmake) @@ -62,6 +85,7 @@ function(__setup_ulp_project app_name project_path prefix s_sources exp_dep_srcs -DULP_APP_NAME=${app_name} -DADD_PICOLIBC_SPECS=${CONFIG_LIBC_PICOLIBC} -DULP_VAR_PREFIX=${prefix} + -DULP_TYPE=${type} -DCOMPONENT_DIR=${COMPONENT_DIR} -DCOMPONENT_INCLUDES=$ -DIDF_TARGET=${idf_target} @@ -93,15 +117,46 @@ function(__setup_ulp_project app_name project_path prefix s_sources exp_dep_srcs endif() endfunction() +function(validate_ulp_type ULP_TYPE) + if(CONFIG_ULP_COPROC_ENABLED) + if(NOT CONFIG_ULP_COPROC_TYPE_FSM AND NOT CONFIG_ULP_COPROC_TYPE_RISCV AND NOT CONFIG_ULP_COPROC_TYPE_LP_CORE) + message(FATAL_ERROR "ULP co-processor is enabled (CONFIG_ULP_COPROC_ENABLED=y), but no ULP type " + "is selected. Please enable at least one of: CONFIG_ULP_COPROC_TYPE_FSM, " + "CONFIG_ULP_COPROC_TYPE_RISCV, or CONFIG_ULP_COPROC_TYPE_LP_CORE in menuconfig.") + endif() + endif() + + if(NOT ULP_TYPE) + # If the user didn't pass TYPE here and both ULP types are enabled in sdkconfig, + # require an explicit TYPE to decide which toolchain to use. + if((CONFIG_ULP_COPROC_TYPE_RISCV STREQUAL "y") AND (CONFIG_ULP_COPROC_TYPE_FSM STREQUAL "y")) + message(FATAL_ERROR "Both CONFIG_ULP_COPROC_TYPE_FSM and CONFIG_ULP_COPROC_TYPE_RISCV are enabled " + "in menuconfig, but calling ulp_embed_binary without TYPE, so the toolchain to use " + "is ambiguous. Call ulp_embed_binary(... TYPE fsm) or ulp_embed_binary(... TYPE riscv)" + " to select the desired ULP type in your CMakeLists.txt file.") + endif() + endif() +endfunction() + function(ulp_embed_binary app_name s_sources exp_dep_srcs) - cmake_parse_arguments(ULP "" "PREFIX" "" ${ARGN}) + cmake_parse_arguments(ULP "" "PREFIX;TYPE" "" ${ARGN}) if(NOT ULP_PREFIX) set(ULP_PREFIX "ulp_") endif() + + validate_ulp_type("${ULP_TYPE}") + __setup_ulp_project("${app_name}" "${idf_path}/components/ulp/cmake" - "${ULP_PREFIX}" "${s_sources}" "${exp_dep_srcs}") + "${ULP_PREFIX}" "${ULP_TYPE}" "${s_sources}" "${exp_dep_srcs}") endfunction() function(ulp_add_project app_name project_path) - __setup_ulp_project("${app_name}" "${project_path}" "ulp_" "" "") + cmake_parse_arguments(ULP "" "PREFIX;TYPE" "" ${ARGN}) + if(NOT ULP_PREFIX) + set(ULP_PREFIX "ulp_") + endif() + + validate_ulp_type("${ULP_TYPE}") + + __setup_ulp_project("${app_name}" "${project_path}" "${ULP_PREFIX}" "${ULP_TYPE}" "" "") endfunction() diff --git a/examples/system/ulp/ulp_fsm/ulp_adc/main/CMakeLists.txt b/examples/system/ulp/ulp_fsm/ulp_adc/main/CMakeLists.txt index c53cee29d4..1b8bd324b3 100644 --- a/examples/system/ulp/ulp_fsm/ulp_adc/main/CMakeLists.txt +++ b/examples/system/ulp/ulp_fsm/ulp_adc/main/CMakeLists.txt @@ -18,4 +18,4 @@ set(ulp_exp_dep_srcs "ulp_adc_example_main.c") # # 4. Call function to build ULP binary and embed in project using the argument # values above. -ulp_embed_binary(${ulp_app_name} "${ulp_s_sources}" "${ulp_exp_dep_srcs}") +ulp_embed_binary(${ulp_app_name} "${ulp_s_sources}" "${ulp_exp_dep_srcs}" TYPE fsm) From 13f894799c5744cfb71e77df5e7e17351a7d8702 Mon Sep 17 00:00:00 2001 From: Meet Patel Date: Fri, 6 Feb 2026 16:03:03 +0530 Subject: [PATCH 2/4] feat(ulp): New example to show ulp fsm and riscv used in same app This example shows how to have both ULP FSM and RISCV enabled in kconfig simultaneously, and use them one after another at run time. A new parameter TYPE is passed to ulp_embed_binary() function to specify fsm or riscv in CMakeLists.txt. This way, both ulp_fsm and ulp_riscv source files can be compiled by their respective toolchains under the same project. The example shows ULP FSM incrementing a counter from 0 to 100, ULP RISC-V incrementing from 100 to 500 and main CPU incrementing from 500 to 1500. --- .../counter/.build-test-rules.yml | 7 + .../counter/CMakeLists.txt | 6 + .../ulp_fsm_riscv_combined/counter/README.md | 119 +++++++++++++++++ .../counter/main/CMakeLists.txt | 20 +++ .../counter/main/ulp_fsm/main.S | 44 +++++++ .../main/ulp_fsm_riscv_combined_main.c | 124 ++++++++++++++++++ .../counter/main/ulp_riscv/main.c | 30 +++++ .../counter/pytest_ulp_fsm_riscv_combined.py | 35 +++++ .../counter/sdkconfig.defaults | 11 ++ 9 files changed, 396 insertions(+) create mode 100644 examples/system/ulp/ulp_fsm_riscv_combined/counter/.build-test-rules.yml create mode 100644 examples/system/ulp/ulp_fsm_riscv_combined/counter/CMakeLists.txt create mode 100644 examples/system/ulp/ulp_fsm_riscv_combined/counter/README.md create mode 100644 examples/system/ulp/ulp_fsm_riscv_combined/counter/main/CMakeLists.txt create mode 100644 examples/system/ulp/ulp_fsm_riscv_combined/counter/main/ulp_fsm/main.S create mode 100644 examples/system/ulp/ulp_fsm_riscv_combined/counter/main/ulp_fsm_riscv_combined_main.c create mode 100644 examples/system/ulp/ulp_fsm_riscv_combined/counter/main/ulp_riscv/main.c create mode 100644 examples/system/ulp/ulp_fsm_riscv_combined/counter/pytest_ulp_fsm_riscv_combined.py create mode 100644 examples/system/ulp/ulp_fsm_riscv_combined/counter/sdkconfig.defaults diff --git a/examples/system/ulp/ulp_fsm_riscv_combined/counter/.build-test-rules.yml b/examples/system/ulp/ulp_fsm_riscv_combined/counter/.build-test-rules.yml new file mode 100644 index 0000000000..4eb9be1fb9 --- /dev/null +++ b/examples/system/ulp/ulp_fsm_riscv_combined/counter/.build-test-rules.yml @@ -0,0 +1,7 @@ +examples/system/ulp/ulp_fsm_riscv_combined/counter: + enable: + - if: SOC_ULP_FSM_SUPPORTED == 1 and SOC_RISCV_COPROC_SUPPORTED == 1 and IDF_TARGET in ["esp32s2", "esp32s3"] + temporary: false + reason: Only ESP32-S2 and ESP32-S3 support both FSM and RISC-V ULP coprocessors + depends_components: + - ulp diff --git a/examples/system/ulp/ulp_fsm_riscv_combined/counter/CMakeLists.txt b/examples/system/ulp/ulp_fsm_riscv_combined/counter/CMakeLists.txt new file mode 100644 index 0000000000..973ba637fd --- /dev/null +++ b/examples/system/ulp/ulp_fsm_riscv_combined/counter/CMakeLists.txt @@ -0,0 +1,6 @@ +# The following lines of boilerplate have to be in your project's +# CMakeLists in this exact order for cmake to work correctly +cmake_minimum_required(VERSION 3.16) + +include($ENV{IDF_PATH}/tools/cmake/project.cmake) +project(ulp_fsm_riscv_combined) diff --git a/examples/system/ulp/ulp_fsm_riscv_combined/counter/README.md b/examples/system/ulp/ulp_fsm_riscv_combined/counter/README.md new file mode 100644 index 0000000000..ef4e1b6383 --- /dev/null +++ b/examples/system/ulp/ulp_fsm_riscv_combined/counter/README.md @@ -0,0 +1,119 @@ +| Supported Targets | ESP32-S2 | ESP32-S3 | +| ----------------- | -------- | -------- | + +# ULP FSM and RISC-V Combined Example + +This example demonstrates how to use both ULP FSM and ULP RISC-V coprocessors sequentially in the same application. + +## Overview + +The example implements a counter that is incremented in three stages: + +1. **ULP FSM Stage**: The FSM coprocessor increments the counter from 0 to 100 +2. **ULP RISC-V Stage**: The RISC-V coprocessor continues incrementing from 100 to 500 +3. **Main CPU Stage**: The main processor completes the count from 500 to 1500 + +This demonstrates: +- Enabling both `CONFIG_ULP_COPROC_TYPE_FSM` and `CONFIG_ULP_COPROC_TYPE_RISCV` for sequential use +- Using the `TYPE` parameter in `ulp_embed_binary()` to specify which toolchain to use for each ULP program +- Coordinating execution between FSM ULP, RISC-V ULP, and the main CPU +- Sharing data between different coprocessors and the main CPU + +## How to Use Example + +### Hardware Required + +* ESP32-S2 or ESP32-S3 development board + +### Build and Flash + +Build the project and flash it to the board, then run monitor tool to view serial output: + +``` +idf.py -p PORT build flash monitor +``` + +(Replace PORT with the name of the serial port to use) + +(To exit the serial monitor, type ``Ctrl-]``) + +See the Getting Started Guide for full steps to configure and use ESP-IDF to build projects. + +## Example Output + +``` +ULP FSM and RISC-V Combined Example +==================================== + +Step 1: Starting ULP FSM to count from 0 to 100... +HP core going to sleep, will be woken by ULP FSM when counting completes... +HP core woken up by: ULP +ULP FSM completed. Counter value: 100 + +Step 2: Starting ULP RISC-V to count from 100 to 500... +HP core going to sleep, will be woken by ULP RISC-V when counting completes... +HP core woken up by: ULP +ULP RISC-V completed. Counter value: 500 + +Step 3: Main CPU counting from 500 to 1500... +Main CPU completed. Final counter value: 1500 + +==================================== +All stages completed successfully! +FSM: 0 -> 100 +RISC-V: 100 -> 500 +Main CPU: 500 -> 1500 +``` + +## Troubleshooting + +* If you see an error about missing ULP type when building, ensure both `CONFIG_ULP_COPROC_TYPE_FSM` and `CONFIG_ULP_COPROC_TYPE_RISCV` are enabled in menuconfig. + +* The `TYPE` parameter in `ulp_embed_binary()` is mandatory when both ULP types are enabled. Check `main/CMakeLists.txt` to see the correct syntax: + ```cmake + ulp_embed_binary(ulp_fsm_main "${ulp_fsm_sources}" "" TYPE fsm) + ulp_embed_binary(ulp_riscv_main "${ulp_riscv_sources}" "" TYPE riscv) + ``` + +## Implementation Details + +### CMakeLists.txt + +The main component's CMakeLists.txt shows how to embed both FSM and RISC-V ULP binaries: + +```cmake +# ULP FSM +set(ulp_fsm_app_name ulp_fsm_main) +set(ulp_fsm_sources "ulp_fsm/main.S") +ulp_embed_binary(${ulp_fsm_app_name} "${ulp_fsm_sources}" "" TYPE fsm) + +# ULP RISC-V +set(ulp_riscv_app_name ulp_riscv_main) +set(ulp_riscv_sources "ulp_riscv/main.c") +ulp_embed_binary(${ulp_riscv_app_name} "${ulp_riscv_sources}" "" TYPE riscv) +``` + +The `TYPE` parameter is crucial - it tells the build system which toolchain to use for each ULP program. + +### ULP FSM Program + +The FSM program (`ulp_fsm/main.S`) is written in assembly and: +- Maintains a counter in RTC slow memory +- Increments it on each wakeup +- Halts when reaching 100 + +### ULP RISC-V Program + +The RISC-V program (`ulp_riscv/main.c`) is written in C and: +- Continues from where FSM left off +- Increments the counter on each wakeup +- Halts when reaching 500 + +### Main Application + +The main application coordinates the three stages: +1. Loads and starts the FSM ULP program +2. Waits for FSM to complete +3. Transfers the counter value and starts the RISC-V ULP program +4. Waits for RISC-V to complete +5. Completes the final counting stage on the main CPU diff --git a/examples/system/ulp/ulp_fsm_riscv_combined/counter/main/CMakeLists.txt b/examples/system/ulp/ulp_fsm_riscv_combined/counter/main/CMakeLists.txt new file mode 100644 index 0000000000..944da33b8d --- /dev/null +++ b/examples/system/ulp/ulp_fsm_riscv_combined/counter/main/CMakeLists.txt @@ -0,0 +1,20 @@ +idf_component_register(SRCS "ulp_fsm_riscv_combined_main.c" + INCLUDE_DIRS "") + +# +# ULP FSM support additions to component CMakeLists.txt. +# + +set(ulp_fsm_app_name ulp_fsm_main) +set(ulp_fsm_sources "ulp_fsm/main.S") + +ulp_embed_binary(${ulp_fsm_app_name} "${ulp_fsm_sources}" "" TYPE fsm) + +# +# ULP RISC-V support additions to component CMakeLists.txt. +# + +set(ulp_riscv_app_name ulp_riscv_main) +set(ulp_riscv_sources "ulp_riscv/main.c") + +ulp_embed_binary(${ulp_riscv_app_name} "${ulp_riscv_sources}" "" TYPE riscv) diff --git a/examples/system/ulp/ulp_fsm_riscv_combined/counter/main/ulp_fsm/main.S b/examples/system/ulp/ulp_fsm_riscv_combined/counter/main/ulp_fsm/main.S new file mode 100644 index 0000000000..652b99ecbf --- /dev/null +++ b/examples/system/ulp/ulp_fsm_riscv_combined/counter/main/ulp_fsm/main.S @@ -0,0 +1,44 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +/* ULP FSM assembly program to increment counter */ + +#include "soc/rtc_cntl_reg.h" +#include "soc/soc_ulp.h" + + /* Define variables */ + .bss + .global fsm_counter +fsm_counter: + .long 0 + + /* Code section */ + .text + .global entry +entry: + /* Load counter address */ + move r1, fsm_counter + + /* Load current counter value */ + ld r0, r1, 0 + + /* Check if counter >= 100 */ + jumpr done, 100, ge + + /* Increment counter */ + add r0, r0, 1 + + /* Store incremented value */ + st r0, r1, 0 + + /* Halt until next wakeup */ + halt + +done: + /* Counter reached 100, wake up the main processor */ + wake + /* Halt - task complete */ + halt diff --git a/examples/system/ulp/ulp_fsm_riscv_combined/counter/main/ulp_fsm_riscv_combined_main.c b/examples/system/ulp/ulp_fsm_riscv_combined/counter/main/ulp_fsm_riscv_combined_main.c new file mode 100644 index 0000000000..ea15ec0548 --- /dev/null +++ b/examples/system/ulp/ulp_fsm_riscv_combined/counter/main/ulp_fsm_riscv_combined_main.c @@ -0,0 +1,124 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +#include +#include +#include "esp_sleep.h" +#include "soc/rtc_cntl_reg.h" +#include "soc/sens_reg.h" +#include "soc/rtc_periph.h" +#include "driver/gpio.h" +#include "driver/rtc_io.h" +#include "ulp.h" +#include "ulp_riscv.h" +#include "ulp_fsm_main.h" +#include "ulp_riscv_main.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +extern const uint8_t ulp_fsm_main_bin_start[] asm("_binary_ulp_fsm_main_bin_start"); +extern const uint8_t ulp_fsm_main_bin_end[] asm("_binary_ulp_fsm_main_bin_end"); + +extern const uint8_t ulp_riscv_main_bin_start[] asm("_binary_ulp_riscv_main_bin_start"); +extern const uint8_t ulp_riscv_main_bin_end[] asm("_binary_ulp_riscv_main_bin_end"); + +static void init_ulp_fsm_program(void); +static void init_ulp_riscv_program(void); + +void app_main(void) +{ + /* Initialize shared counter */ + ulp_fsm_counter = 0; + ulp_riscv_counter = 0; + + printf("ULP FSM and RISC-V Combined Example\n"); + printf("====================================\n\n"); + + /* Enable ULP wakeup */ + ESP_ERROR_CHECK(esp_sleep_enable_ulp_wakeup()); + + /* Step 1: Start ULP FSM to count from 0 to 100 */ + printf("Step 1: Starting ULP FSM to count from 0 to 100...\n"); + printf("HP core going to sleep, will be woken by ULP FSM when counting completes...\n"); + init_ulp_fsm_program(); + + /* Enter light sleep - ULP FSM will wake us when done */ + esp_light_sleep_start(); + + /* Woken up by ULP FSM */ + uint32_t wakeup_causes = esp_sleep_get_wakeup_causes(); + printf("HP core woken up by: %s\n", (wakeup_causes & (1U << ESP_SLEEP_WAKEUP_ULP)) ? "ULP" : "other"); + + /* Stop FSM ULP */ + ulp_timer_stop(); + uint32_t fsm_final_count = ulp_fsm_counter; // Save FSM result before RISC-V overwrites it + printf("ULP FSM completed. Counter value: %" PRIu32 "\n\n", fsm_final_count); + + /* Small delay to ensure FSM is fully stopped before starting RISC-V */ + vTaskDelay(pdMS_TO_TICKS(100)); + + /* Step 2: Start ULP RISC-V to count from 100 to 500 */ + printf("Step 2: Starting ULP RISC-V to count from 100 to 500...\n"); + printf("HP core going to sleep, will be woken by ULP RISC-V when counting completes...\n"); + init_ulp_riscv_program(); + + /* Transfer counter value to RISC-V ULP (after loading binary, otherwise it will be overwritten) */ + ulp_riscv_counter = fsm_final_count; + + /* Enter light sleep - ULP RISC-V will wake us when done */ + esp_light_sleep_start(); + + /* Woken up by ULP RISC-V */ + wakeup_causes = esp_sleep_get_wakeup_causes(); + printf("HP core woken up by: %s\n", (wakeup_causes & (1U << ESP_SLEEP_WAKEUP_ULP)) ? "ULP" : "other"); + + /* Stop RISC-V ULP */ + ulp_riscv_timer_stop(); + uint32_t riscv_final_count = ulp_riscv_counter; // Save RISC-V result + printf("ULP RISC-V completed. Counter value: %" PRIu32 "\n\n", riscv_final_count); + + /* Stage 3: Main CPU counts from 500 to 1500 */ + printf("Step 3: Main CPU counting from 500 to 1500...\n"); + uint32_t counter = riscv_final_count; + while (counter < 1500) { + counter++; + } + printf("Main CPU completed. Final counter value: %" PRIu32 "\n\n", counter); + + printf("====================================\n"); + printf("All stages completed successfully!\n"); + printf("FSM: 0 -> %" PRIu32 "\n", fsm_final_count); + printf("RISC-V: %" PRIu32 " -> %" PRIu32 "\n", fsm_final_count, riscv_final_count); + printf("Main CPU: %" PRIu32 " -> %" PRIu32 "\n", riscv_final_count, counter); +} + +static void init_ulp_fsm_program(void) +{ + esp_err_t err = ulp_load_binary(0, ulp_fsm_main_bin_start, + (ulp_fsm_main_bin_end - ulp_fsm_main_bin_start) / sizeof(uint32_t)); + ESP_ERROR_CHECK(err); + + /* Set ULP wake up period to 10ms */ + ulp_set_wakeup_period(0, 10000); + + /* Start the ULP FSM program */ + err = ulp_run(&ulp_entry - RTC_SLOW_MEM); + ESP_ERROR_CHECK(err); +} + +static void init_ulp_riscv_program(void) +{ + esp_err_t err = ulp_riscv_load_binary(ulp_riscv_main_bin_start, + (ulp_riscv_main_bin_end - ulp_riscv_main_bin_start)); + ESP_ERROR_CHECK(err); + + /* Set ULP wake up period to 1ms */ + ulp_set_wakeup_period(0, 1000); + + /* Start the ULP RISC-V program */ + err = ulp_riscv_run(); + ESP_ERROR_CHECK(err); +} diff --git a/examples/system/ulp/ulp_fsm_riscv_combined/counter/main/ulp_riscv/main.c b/examples/system/ulp/ulp_fsm_riscv_combined/counter/main/ulp_riscv/main.c new file mode 100644 index 0000000000..b6836be7cd --- /dev/null +++ b/examples/system/ulp/ulp_fsm_riscv_combined/counter/main/ulp_riscv/main.c @@ -0,0 +1,30 @@ +/* + * SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ + +/* ULP RISC-V program to increment counter */ + +#include +#include "ulp_riscv.h" +#include "ulp_riscv_utils.h" + +uint32_t riscv_counter = 0; + +int main(void) +{ + /* Check if counting is complete */ + if (riscv_counter >= 500) { + /* Wake up the main processor and halt */ + ulp_riscv_wakeup_main_processor(); + ulp_riscv_halt(); + return 0; + } + + /* Increment counter once per wakeup */ + riscv_counter++; + + /* Continue - will be woken up again by timer */ + return 0; +} diff --git a/examples/system/ulp/ulp_fsm_riscv_combined/counter/pytest_ulp_fsm_riscv_combined.py b/examples/system/ulp/ulp_fsm_riscv_combined/counter/pytest_ulp_fsm_riscv_combined.py new file mode 100644 index 0000000000..cf1cba672d --- /dev/null +++ b/examples/system/ulp/ulp_fsm_riscv_combined/counter/pytest_ulp_fsm_riscv_combined.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python +# +# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: Unlicense OR CC0-1.0 + +import pytest +from pytest_embedded import Dut +from pytest_embedded_idf.utils import idf_parametrize + + +@pytest.mark.generic +@idf_parametrize('target', ['esp32s2', 'esp32s3'], indirect=['target']) +def test_ulp_fsm_riscv_combined(dut: Dut) -> None: + dut.expect('ULP FSM and RISC-V Combined Example', timeout=10) + + # Check Step 1: FSM counting + dut.expect('Step 1: Starting ULP FSM to count from 0 to 100', timeout=5) + dut.expect('HP core going to sleep, will be woken by ULP FSM when counting completes', timeout=5) + dut.expect('HP core woken up by: ULP', timeout=5) + dut.expect('ULP FSM completed. Counter value: 100', timeout=5) + + # Check Step 2: RISC-V counting + dut.expect('Step 2: Starting ULP RISC-V to count from 100 to 500', timeout=5) + dut.expect('HP core going to sleep, will be woken by ULP RISC-V when counting completes', timeout=5) + dut.expect('HP core woken up by: ULP', timeout=5) + dut.expect('ULP RISC-V completed. Counter value: 500', timeout=5) + + # Check Step 3: Main CPU counting + dut.expect('Step 3: Main CPU counting from 500 to 1500', timeout=5) + dut.expect('Main CPU completed. Final counter value: 1500', timeout=5) + # Verify success message + dut.expect('All stages completed successfully!', timeout=5) + dut.expect('FSM: 0 -> 100', timeout=5) + dut.expect('RISC-V: 100 -> 500', timeout=5) + dut.expect('Main CPU: 500 -> 1500', timeout=5) diff --git a/examples/system/ulp/ulp_fsm_riscv_combined/counter/sdkconfig.defaults b/examples/system/ulp/ulp_fsm_riscv_combined/counter/sdkconfig.defaults new file mode 100644 index 0000000000..07184040c8 --- /dev/null +++ b/examples/system/ulp/ulp_fsm_riscv_combined/counter/sdkconfig.defaults @@ -0,0 +1,11 @@ +# Enable both ULP types +CONFIG_ULP_COPROC_ENABLED=y +CONFIG_ULP_COPROC_TYPE_FSM=y +CONFIG_ULP_COPROC_TYPE_RISCV=y +CONFIG_ULP_COPROC_RESERVE_MEM=4096 + +# Set log level to Info to see output +CONFIG_BOOTLOADER_LOG_LEVEL_INFO=y +CONFIG_BOOTLOADER_LOG_LEVEL=3 +CONFIG_LOG_DEFAULT_LEVEL_INFO=y +CONFIG_LOG_DEFAULT_LEVEL=3 From 5df38b48f426176e1a53b7ef61d3ed3c6d27a5c2 Mon Sep 17 00:00:00 2001 From: Meet Patel Date: Tue, 10 Feb 2026 15:02:36 +0530 Subject: [PATCH 3/4] docs(ulp): Updated the docs for ULP fsm and ULP riscv support Updated the documentation of ULP, as we have updated kconfig options and build files to allow selecting both ULP FSM and ULP RISCV simultaneously at compile time. --- docs/en/api-reference/system/ulp-fsm.rst | 10 +++++++--- docs/en/api-reference/system/ulp-lp-core.rst | 2 +- docs/en/api-reference/system/ulp-risc-v.rst | 12 ++++++++---- docs/en/api-reference/system/ulp.rst | 2 ++ 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/docs/en/api-reference/system/ulp-fsm.rst b/docs/en/api-reference/system/ulp-fsm.rst index c07be98a9d..f120b0ab9f 100644 --- a/docs/en/api-reference/system/ulp-fsm.rst +++ b/docs/en/api-reference/system/ulp-fsm.rst @@ -47,9 +47,9 @@ To compile the ULP FSM code as part of the component, the following steps must b set(ulp_s_sources ulp/ulp_assembly_source_file.S) set(ulp_exp_dep_srcs "ulp_c_source_file.c") - ulp_embed_binary(${ulp_app_name} "${ulp_s_sources}" "${ulp_exp_dep_srcs}") + ulp_embed_binary(${ulp_app_name} "${ulp_s_sources}" "${ulp_exp_dep_srcs}" TYPE fsm) -The first argument to ``ulp_embed_binary`` specifies the ULP FSM binary name. The name specified here will also be used by other generated artifacts such as the ELF file, map file, header file and linker export file. The second argument specifies the ULP FSM assembly source files. Finally, the third argument specifies the list of component source files which include the header file to be generated. This list is needed to build the dependencies correctly and ensure that the generated header file will be created before any of these files are compiled. See the section below for the concept of generated header files for ULP applications. +The first argument to ``ulp_embed_binary`` specifies the ULP FSM binary name. The name specified here will also be used by other generated artifacts such as the ELF file, map file, header file and linker export file. The second argument specifies the ULP FSM assembly source files. The third argument specifies the list of component source files which include the header file to be generated. This list is needed to build the dependencies correctly and ensure that the generated header file will be created before any of these files are compiled. Finally, the fourth argument ``TYPE`` is optional, but it must be provided as ``TYPE fsm`` to compile using FSM toolchain, if both ``CONFIG_ULP_COPROC_TYPE_FSM`` and ``CONFIG_ULP_COPROC_TYPE_RISCV`` are selected in menu option ``ULP Coprocessor types``. See the section below for the concept of generated header files for ULP applications. Variables in the ULP code will be prefixed with ``ulp_`` (default value) in this generated header file. @@ -135,7 +135,7 @@ Starting the ULP FSM Program To run a ULP FSM program, the main application needs to load the ULP program into RTC memory using the :cpp:func:`ulp_load_binary` function, and then start it using the :cpp:func:`ulp_run` function. -Note that the ``Enable Ultra Low Power (ULP) Coprocessor`` option must be enabled in menuconfig to work with ULP. To select the type of ULP to be used, the ``ULP Co-processor type`` option must be set. To reserve memory for the ULP, the ``RTC slow memory reserved for coprocessor`` option must be set to a value big enough to store ULP code and data. If the application components contain multiple ULP programs, then the size of the RTC memory must be sufficient to hold the largest one. +Note that the ``Enable Ultra Low Power (ULP) Coprocessor`` option must be enabled in menuconfig to work with ULP. To select the FSM type for ULP, please go to menu option ``ULP Coprocessor types`` and select ``CONFIG_ULP_COPROC_TYPE_FSM``. To reserve memory for the ULP, the ``RTC slow memory reserved for coprocessor`` option must be set to a value big enough to store ULP code and data. If the application components contain multiple ULP programs, then the size of the RTC memory must be sufficient to hold the largest one. Each ULP program is embedded into the ESP-IDF application as a binary blob. The application can reference this blob and load it in the following way (suppose ULP_APP_NAME was defined to ``ulp_app_name``):: @@ -200,6 +200,10 @@ Application Examples * :example:`system/ulp/ulp_fsm/ulp_adc` demonstrates how to use the ULP FSM coprocessor to periodically measure input voltage on a specific ADC channel during deep sleep, compare it to the set threshold, and wake up the system if the voltage is outside the threshold. +.. only:: esp32s2 or esp32s3 + + * :example:`system/ulp/ulp_fsm_riscv_combined/counter` demonstrates how to use both the ULP FSM and ULP RISC-V coprocessors sequentially within the same application, counting from 0 to 100 with the FSM and from 100 to 500 with the RISC-V coprocessor while the HP core remains in sleep mode until the counting completes. + API Reference ------------- diff --git a/docs/en/api-reference/system/ulp-lp-core.rst b/docs/en/api-reference/system/ulp-lp-core.rst index 8dd8022b88..9b333d82f7 100644 --- a/docs/en/api-reference/system/ulp-lp-core.rst +++ b/docs/en/api-reference/system/ulp-lp-core.rst @@ -105,7 +105,7 @@ Building Your Project To compile and build your project: -1. Enable both :ref:`CONFIG_ULP_COPROC_ENABLED` and :ref:`CONFIG_ULP_COPROC_TYPE` in menuconfig, and set :ref:`CONFIG_ULP_COPROC_TYPE` to ``CONFIG_ULP_COPROC_TYPE_LP_CORE``. The :ref:`CONFIG_ULP_COPROC_RESERVE_MEM` option reserves RTC memory for the ULP, and must be set to a value big enough to store both the ULP LP core code and data. If the application components contain multiple ULP programs, then the size of the RTC memory must be sufficient to hold the largest one. +1. Enable :ref:`CONFIG_ULP_COPROC_ENABLED` in menuconfig, and inside ``ULP Coprocessor types`` menu, select :ref:`CONFIG_ULP_COPROC_TYPE_LP_CORE`. The :ref:`CONFIG_ULP_COPROC_RESERVE_MEM` option reserves RTC memory for the ULP, and must be set to a value big enough to store both the ULP LP core code and data. If the application components contain multiple ULP programs, then the size of the RTC memory must be sufficient to hold the largest one. 2. Build the application as usual (e.g., ``idf.py app``). diff --git a/docs/en/api-reference/system/ulp-risc-v.rst b/docs/en/api-reference/system/ulp-risc-v.rst index cc0dc56f9d..455151f398 100644 --- a/docs/en/api-reference/system/ulp-risc-v.rst +++ b/docs/en/api-reference/system/ulp-risc-v.rst @@ -36,9 +36,9 @@ Using ``ulp_embed_binary`` set(ulp_sources "ulp/ulp_c_source_file.c" "ulp/ulp_assembly_source_file.S") set(ulp_exp_dep_srcs "ulp_c_source_file.c") - ulp_embed_binary(${ulp_app_name} "${ulp_sources}" "${ulp_exp_dep_srcs}") + ulp_embed_binary(${ulp_app_name} "${ulp_sources}" "${ulp_exp_dep_srcs}" TYPE riscv) -The first argument to ``ulp_embed_binary`` specifies the ULP binary name. The name specified here is also used by other generated artifacts such as the ELF file, map file, header file, and linker export file. The second argument specifies the ULP source files. Finally, the third argument specifies the list of component source files which include the header file to be generated. This list is needed to build the dependencies correctly and ensure that the generated header file is created before any of these files are compiled. See the section below for the concept of generated header files for ULP applications. +The first argument to ``ulp_embed_binary`` specifies the ULP binary name. The name specified here is also used by other generated artifacts such as the ELF file, map file, header file, and linker export file. The second argument specifies the ULP source files. The third argument specifies the list of component source files which include the header file to be generated. This list is needed to build the dependencies correctly and ensure that the generated header file is created before any of these files are compiled. Finally, The fourth argument ``TYPE`` is optional, but it must be provided as ``TYPE riscv`` to compile using risc-v toolchain, if both :ref:`CONFIG_ULP_COPROC_TYPE_FSM` and :ref:`CONFIG_ULP_COPROC_TYPE_RISCV` are selected in menu option ``ULP Coprocessor types``. See the section below for the concept of generated header files for ULP applications. Variables in the ULP code will be prefixed with ``ulp_`` (default value) in this generated header file. @@ -108,7 +108,7 @@ Building Your Project To compile and build your project: -1. Enable both :ref:`CONFIG_ULP_COPROC_ENABLED` and :ref:`CONFIG_ULP_COPROC_TYPE` in menuconfig, and set :ref:`CONFIG_ULP_COPROC_TYPE` to ``CONFIG_ULP_COPROC_TYPE_LP_CORE``. The :ref:`CONFIG_ULP_COPROC_RESERVE_MEM` option reserves RTC memory for the ULP, and must be set to a value big enough to store both the ULP LP-Core code and data. If the application components contain multiple ULP programs, then the size of the RTC memory must be sufficient to hold the largest one. +1. Enable :ref:`CONFIG_ULP_COPROC_ENABLED` in menuconfig, and inside ``ULP Coprocessor types`` menu, select :ref:`CONFIG_ULP_COPROC_TYPE_RISCV`. The :ref:`CONFIG_ULP_COPROC_RESERVE_MEM` option reserves RTC memory for the ULP, and must be set to a value big enough to store both the ULP RISC-V code and data. If the application components contain multiple ULP programs, then the size of the RTC memory must be sufficient to hold the largest one. 2. Build the application as usual (e.g., ``idf.py app``). @@ -193,7 +193,7 @@ Starting the ULP RISC-V Program To run a ULP RISC-V program, the main application needs to load the ULP program into RTC memory using the :cpp:func:`ulp_riscv_load_binary` function, and then start it using the :cpp:func:`ulp_riscv_run` function. -Note that the ``CONFIG_ULP_COPROC_ENABLED`` and ``CONFIG_ULP_COPROC_TYPE_RISCV`` options must be enabled in menuconfig to work with ULP RISC-V. To reserve memory for the ULP, the ``RTC slow memory reserved for coprocessor`` option must be set to a value big enough to store ULP RISC-V code and data. If the application components contain multiple ULP programs, then the size of the RTC memory must be sufficient to hold the largest one. +Note that the :ref:`CONFIG_ULP_COPROC_ENABLED` and :ref:`CONFIG_ULP_COPROC_TYPE_RISCV` options must be enabled in menuconfig to work with ULP RISC-V. To reserve memory for the ULP, the ``RTC slow memory reserved for coprocessor`` option must be set to a value big enough to store ULP RISC-V code and data. If the application components contain multiple ULP programs, then the size of the RTC memory must be sufficient to hold the largest one. Each ULP RISC-V program is embedded into the ESP-IDF application as a binary blob. The application can reference this blob and load it in the following way (suppose ULP_APP_NAME was defined to ``ulp_app_name``): @@ -339,6 +339,10 @@ Application Examples * :example:`system/ulp/ulp_riscv/touch` demonstrates how to program the ULP RISC-V coprocessor to periodically scan and read touch pad sensors, and wake up the main CPU when a touch pad is active. +.. only:: esp32s2 or esp32s3 + + * :example:`system/ulp/ulp_fsm_riscv_combined/counter` demonstrates how to use both the ULP FSM and ULP RISC-V coprocessors sequentially within the same application, counting from 0 to 100 with the FSM and from 100 to 500 with the RISC-V coprocessor while the HP core remains in sleep mode until the counting completes. + API Reference ------------- diff --git a/docs/en/api-reference/system/ulp.rst b/docs/en/api-reference/system/ulp.rst index b1fae3661e..25993ed8e6 100644 --- a/docs/en/api-reference/system/ulp.rst +++ b/docs/en/api-reference/system/ulp.rst @@ -17,6 +17,8 @@ The Ultra Low Power (ULP) coprocessor is designed to perform tasks while the mai - **ULP RISC-V**: A RISC-V based ULP coprocessor. It offers enhanced computational capabilities and flexibility, making it suitable for more complex applications written in C. This type is supported by ESP32-S2 and ESP32-S3. - **ULP LP Core**: A RISC-V based ULP coprocessor that combines the advantages of the **ULP RISC-V** type with additional features, such as extended memory access, broader peripheral access, a debug module, and an interrupt controller. This coprocessor is capable of operating even when the entire system is active. This type is supported by ESP32-C5, ESP32-C6, ESP32-P4, and upcoming chips. + NOTE: Chips like ESP32-S2 and ESP32-S3 support both **ULP FSM** and **ULP RISC-V**. User can enable both of them in menuconfig at compile time, and choose which one to use at run time. + .. toctree:: :maxdepth: 1 From f25237145a34f8c0496effe9f94fd59f3d54e85f Mon Sep 17 00:00:00 2001 From: renpeiying Date: Mon, 2 Mar 2026 15:29:43 +0800 Subject: [PATCH 4/4] docs: Add Chinese translation --- docs/zh_CN/api-reference/system/ulp-fsm.rst | 10 +++++++--- docs/zh_CN/api-reference/system/ulp-lp-core.rst | 4 ++-- docs/zh_CN/api-reference/system/ulp-risc-v.rst | 12 ++++++++---- docs/zh_CN/api-reference/system/ulp.rst | 2 ++ 4 files changed, 19 insertions(+), 9 deletions(-) diff --git a/docs/zh_CN/api-reference/system/ulp-fsm.rst b/docs/zh_CN/api-reference/system/ulp-fsm.rst index 89ad383547..aa827a4953 100644 --- a/docs/zh_CN/api-reference/system/ulp-fsm.rst +++ b/docs/zh_CN/api-reference/system/ulp-fsm.rst @@ -47,9 +47,9 @@ ULP FSM 协处理器代码由汇编语言编写,使用 `binutils-esp32ulp 工 set(ulp_s_sources ulp/ulp_assembly_source_file.S) set(ulp_exp_dep_srcs "ulp_c_source_file.c") - ulp_embed_binary(${ulp_app_name} "${ulp_s_sources}" "${ulp_exp_dep_srcs}") + ulp_embed_binary(${ulp_app_name} "${ulp_s_sources}" "${ulp_exp_dep_srcs}" TYPE fsm) -``ulp_embed_binary`` 的第一个参数用于指定 ULP FSM 二进制文件的名称,该名称也用于生成的其他文件,如:ELF 文件、.map 文件、头文件和链接器导出文件。第二个参数指定 ULP FSM 汇编源文件。最后,第三个参数指定组件源文件列表,这些源文件中会包含要生成的头文件。此列表用于建立正确的依赖项,并确保在编译这些文件之前先创建生成的头文件。有关 ULP FSM 应用程序生成的头文件等相关概念,请参考下文。 +``ulp_embed_binary`` 的第一个参数用于指定 ULP FSM 二进制文件的名称,该名称也用于生成的其他文件,如:ELF 文件、.map 文件、头文件和链接器导出文件。第二个参数指定 ULP FSM 汇编源文件。第三个参数指定组件源文件列表,这些源文件中会包含要生成的头文件。此列表用于建立正确的依赖项,并确保在编译这些文件之前先创建生成的头文件。第四个参数 ``TYPE`` 为可选项,但在 menuconfig 的 ``ULP Coprocessor types`` 中同时勾选 ``CONFIG_ULP_COPROC_TYPE_FSM`` 与 ``CONFIG_ULP_COPROC_TYPE_RISCV`` 时,必须填写为 ``TYPE fsm`` 才能使用 FSM 工具链编译。有关 ULP FSM 应用程序生成的头文件等相关概念,请参考下文。 在这个生成的头文件中,ULP 代码中的变量默认以 ``ulp_`` 作为前缀。 @@ -135,7 +135,7 @@ ULP FSM 协处理器代码由汇编语言编写,使用 `binutils-esp32ulp 工 要运行 ULP FSM 程序,主应用程序需要调用 :cpp:func:`ulp_load_binary` 函数将 ULP 程序加载到 RTC 内存中,然后调用 :cpp:func:`ulp_run` 函数,启动 ULP 程序。 -注意,在 menuconfig 中必须启用 ``Enable Ultra Low Power (ULP) Coprocessor`` 选项,以便正常运行 ULP,并且必须设置 ``ULP Co-processor type`` 选项,以便选择要使用的 ULP 类型。 ``RTC slow memory reserved for coprocessor`` 选项设置的值必须足够储存 ULP 代码和数据。如果应用程序组件包含多个 ULP 程序,则 RTC 内存必须足以容纳最大的程序。 +注意,在 menuconfig 中必须启用 ``Enable Ultra Low Power (ULP) Coprocessor`` 选项才能使用 ULP。要选择 ULP 的 FSM 类型,请进入 menu 选项 ``ULP Coprocessor types`` 并勾选 ``CONFIG_ULP_COPROC_TYPE_FSM``。``RTC slow memory reserved for coprocessor`` 选项设置的值必须足够储存 ULP 代码和数据。如果应用程序组件包含多个 ULP 程序,则 RTC 内存必须足以容纳最大的程序。 每个 ULP 程序均以二进制 BLOB 的形式嵌入到 ESP-IDF 应用程序中。应用程序可以引用此 BLOB,并以下面的方式加载此 BLOB(假设 ULP_APP_NAME 已被定义为 ``ulp_app_name``):: @@ -200,6 +200,10 @@ ULP FSM 协处理器代码由汇编语言编写,使用 `binutils-esp32ulp 工 * :example:`system/ulp/ulp_fsm/ulp_adc` 展示了主处理器处于 Deep-sleep 状态时,ULP FSM 协处理器测量特定 ADC 通道上的输入电压,将其与设定的阈值进行比较,电压超出阈值时唤醒系统。 +.. only:: esp32s2 or esp32s3 + + * :example:`system/ulp/ulp_fsm_riscv_combined/counter` 展示了如何在同一个应用程序中依次使用 ULP FSM 和 ULP RISC-V 协处理器:使用 FSM 从 0 计数到 100,再使用 RISC-V 协处理器从 100 计数到 500。期间,HP 内核保持休眠模式,直到计数完成。 + API 参考 ------------- diff --git a/docs/zh_CN/api-reference/system/ulp-lp-core.rst b/docs/zh_CN/api-reference/system/ulp-lp-core.rst index 2a51dd9618..87b3ce21b3 100644 --- a/docs/zh_CN/api-reference/system/ulp-lp-core.rst +++ b/docs/zh_CN/api-reference/system/ulp-lp-core.rst @@ -105,7 +105,7 @@ ULP LP 内核代码会与 ESP-IDF 项目共同编译,生成一个单独的二 若想编译和构建项目,请执行以下操作: -1. 在 menuconfig 中启用 :ref:`CONFIG_ULP_COPROC_ENABLED` 和 :ref:`CONFIG_ULP_COPROC_TYPE` 选项,并将 :ref:`CONFIG_ULP_COPROC_TYPE` 设置为 ``CONFIG_ULP_COPROC_TYPE_LP_CORE``。:ref:`CONFIG_ULP_COPROC_RESERVE_MEM` 选项为 ULP 保留 RTC 内存,因此必须设置为一个足够大的值,以存储 ULP LP 内核代码和数据。如果应用程序组件包含多个 ULP 程序,那么 RTC 内存的大小必须足够容纳其中最大的程序。 +1. 在 menuconfig 中启用 :ref:`CONFIG_ULP_COPROC_ENABLED`,并在 ``ULP Coprocessor types`` 菜单中勾选 :ref:`CONFIG_ULP_COPROC_TYPE_LP_CORE`。:ref:`CONFIG_ULP_COPROC_RESERVE_MEM` 选项为 ULP 保留 RTC 内存,因此必须设置为一个足够大的值,以存储 ULP LP 内核代码和数据。如果应用程序组件包含多个 ULP 程序,那么 RTC 内存的大小必须足够容纳其中最大的程序。 2. 按照常规步骤构建应用程序(例如 ``idf.py app``)。 @@ -299,7 +299,7 @@ ULP LP 内核的时钟源来自系统时钟 ``LP_FAST_CLK``,详情请参见 ` * 通过共享变量共享程序状态:如 :ref:`ulp-lp-core-access-variables` 所述,主 CPU 和 ULP 内核都可以轻松访问 RTC 内存中的全局变量。若想了解 ULP 内核的运行状态,可以将状态信息从 ULP 写入变量中,并通过主 CPU 读取信息。这种方法的缺点在于它需要主 CPU 一直处于唤醒状态,而这通常很难实现。另外,若主 CPU 一直处于唤醒状态,可能会掩盖某些问题,因为部分问题只会在特定电源域断电时发生。 -* 紧急处理程序:当检测到异常时,LP 内核的紧急处理程序会把 LP 内核寄存器的状态通过 LP UART 发送出去。将 :ref:`CONFIG_ULP_PANIC_OUTPUT_ENABLE` 选项设置为 ``y``,可以启用紧急处理程序。禁用此选项将减少 LP 内核应用程序的 LP-RAM 使用量。若想从紧急转储中解析栈回溯,可以使用 ``idf.py monitor``。 +* 紧急处理程序:当检测到异常时,LP 内核的紧急处理程序会把 LP 内核寄存器的状态通过 LP UART 发送出去。将 ``CONFIG_ULP_PANIC_OUTPUT_ENABLE`` 选项设置为 ``y``,可以启用紧急处理程序。禁用此选项将减少 LP 内核应用程序的 LP-RAM 使用量。若想从紧急转储中解析栈回溯,可以使用 ``idf.py monitor``。 .. warning:: diff --git a/docs/zh_CN/api-reference/system/ulp-risc-v.rst b/docs/zh_CN/api-reference/system/ulp-risc-v.rst index 9599e5122b..089d2f7410 100644 --- a/docs/zh_CN/api-reference/system/ulp-risc-v.rst +++ b/docs/zh_CN/api-reference/system/ulp-risc-v.rst @@ -36,9 +36,9 @@ ULP RISC-V 协处理器代码以 C 语言(或汇编语言)编写,使用基 set(ulp_sources "ulp/ulp_c_source_file.c" "ulp/ulp_assembly_source_file.S") set(ulp_exp_dep_srcs "ulp_c_source_file.c") - ulp_embed_binary(${ulp_app_name} "${ulp_sources}" "${ulp_exp_dep_srcs}") + ulp_embed_binary(${ulp_app_name} "${ulp_sources}" "${ulp_exp_dep_srcs}" TYPE riscv) -``ulp_embed_binary`` 的第一个参数指定生成的 ULP 二进制文件名。该文件名也用于其他生成的文件,如 ELF 文件、映射文件、头文件和链接器导出文件。第二个参数指定 ULP 源文件。第三个参数指定组件源文件列表,其中包括生成的头文件。此列表用以正确构建依赖,并确保在编译这些文件前创建要生成的头文件。有关 ULP 应用程序生成头文件的概念,请参阅本文档后续章节。 +``ulp_embed_binary`` 的第一个参数指定生成的 ULP 二进制文件名。该文件名也用于其他生成的文件,如 ELF 文件、映射文件、头文件和链接器导出文件。第二个参数指定 ULP 源文件。第三个参数指定组件源文件列表,其中包括生成的头文件。此列表用以正确构建依赖,并确保在编译这些文件前创建要生成的头文件。第四个参数 ``TYPE`` 为可选项,但在 menu 选项 ``ULP Coprocessor types`` 中同时勾选 :ref:`CONFIG_ULP_COPROC_TYPE_FSM` 与 :ref:`CONFIG_ULP_COPROC_TYPE_RISCV` 时,必须填写为 ``TYPE riscv`` 才能使用 RISC-V 工具链编译。有关 ULP 应用程序生成头文件的概念,请参阅本文档后续章节。 在这个生成的头文件中,ULP 代码中的变量默认以 ``ulp_`` 作为前缀。 @@ -108,7 +108,7 @@ ULP RISC-V 协处理器代码以 C 语言(或汇编语言)编写,使用基 若想编译和构建项目,请执行以下操作: -1. 在 menuconfig 中启用 :ref:`CONFIG_ULP_COPROC_ENABLED` 和 :ref:`CONFIG_ULP_COPROC_TYPE` 选项,并将 :ref:`CONFIG_ULP_COPROC_TYPE` 设置为 ``CONFIG_ULP_COPROC_TYPE_LP_CORE``。:ref:`CONFIG_ULP_COPROC_RESERVE_MEM` 选项为 ULP 保留 RTC 内存,因此必须设置为一个足够大的值,以存储 ULP LP-Core 代码和数据。如果应用程序组件包含多个 ULP 程序,那么 RTC 内存的大小必须足够容纳其中最大的程序。 +1. 在 menuconfig 中启用 :ref:`CONFIG_ULP_COPROC_ENABLED`,并在 ``ULP Coprocessor types`` 菜单中勾选 `CONFIG_ULP_COPROC_TYPE_RISCV`。:ref:`CONFIG_ULP_COPROC_RESERVE_MEM` 选项为 ULP 保留 RTC 内存,因此必须设置为一个足够大的值,以存储 ULP RISC-V 代码和数据。如果应用程序组件包含多个 ULP 程序,那么 RTC 内存的大小必须足够容纳其中最大的程序。 2. 按照常规步骤构建应用程序(例如 ``idf.py app``)。 @@ -193,7 +193,7 @@ ULP 中的所有硬件指令都不支持互斥,所以 Lock API 需通过一种 要运行 ULP RISC-V 程序,主程序需要调用 :cpp:func:`ulp_riscv_load_binary` 函数,将 ULP 程序加载到 RTC 内存中,然后调用 :cpp:func:`ulp_riscv_run` 函数,启动 ULP RISC-V 程序。 -注意,必须在 menuconfig 中启用 ``CONFIG_ULP_COPROC_ENABLED`` 和 ``CONFIG_ULP_COPROC_TYPE_RISCV`` 选项,以便正常运行 ULP RISC-V 程序。``RTC slow memory reserved for coprocessor`` 选项设置的值必须足够存储 ULP RISC-V 代码和数据。如果应用程序组件包含多个 ULP 程序,RTC 内存必须足以容纳最大的程序。 +注意,必须在 menuconfig 中启用 :ref:`CONFIG_ULP_COPROC_ENABLED` 和 :ref:`CONFIG_ULP_COPROC_TYPE_RISCV` 选项才能使用 ULP RISC-V。 ``RTC slow memory reserved for coprocessor`` 选项设置的值必须足够存储 ULP RISC-V 代码和数据。如果应用程序组件包含多个 ULP 程序,RTC 内存必须足以容纳最大的程序。 每个 ULP RISC-V 程序均以二进制 BLOB 的形式嵌入到 ESP-IDF 应用程序中。应用程序可以引用此 BLOB,并以下面的方式加载此 BLOB(假设 ULP_APP_NAME 已被定义为 ``ulp_app_name``): @@ -339,6 +339,10 @@ ULP RISC-V 的中断处理尚在开发中,还不支持针对内部中断源的 * :example:`system/ulp/ulp_riscv/touch` 演示了如何使用 ULP RISC-V 协处理器定期扫描和读取触摸传感器,并在触摸传感器被激活时唤醒主 CPU。 +.. only:: esp32s2 or esp32s3 + + * :example:`system/ulp/ulp_fsm_riscv_combined/counter` 展示了如何在同一个应用程序中依次使用 ULP FSM 和 ULP RISC-V 协处理器:使用 FSM 从 0 计数到 100,再使用 RISC-V 协处理器从 100 计数到 500。期间,HP 内核保持休眠模式,直到计数完成。 + API 参考 ------------- diff --git a/docs/zh_CN/api-reference/system/ulp.rst b/docs/zh_CN/api-reference/system/ulp.rst index 1bbc0fc405..08c4a8e076 100644 --- a/docs/zh_CN/api-reference/system/ulp.rst +++ b/docs/zh_CN/api-reference/system/ulp.rst @@ -17,6 +17,8 @@ ULP(Ultra Low Power,超低功耗)协处理器可在主 CPU 处于深度睡 - **ULP RISC-V**:基于 RISC-V 架构的协处理器。提供更强的计算能力和灵活性,适合运行用 C 语言编写的复杂应用。ESP32-S2 和 ESP32-S3 支持此类型。 - **ULP LP Core**:基于 RISC-V 架构的增强型协处理器,兼具 **ULP RISC-V** 的优势,并新增扩展内存访问、更广泛的外设访问、调试模块和中断控制器等功能。该处理器甚至可在全系统运行时工作。ESP32-C5、ESP32-C6、ESP32-P4 及后续芯片支持此类型。 + 注意:ESP32-S2、ESP32-S3 等芯片同时支持 **ULP FSM** 和 **ULP RISC-V**。用户可在编译时在 menuconfig 中同时启用二者,并在运行时选择使用其中一种。 + .. toctree:: :maxdepth: 1