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
This commit is contained in:
Meet Patel
2026-02-04 12:08:28 +05:30
parent 3c499632c7
commit a3f167f1c4
6 changed files with 154 additions and 60 deletions
+15 -8
View File
@@ -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
+2 -1
View File
@@ -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"
+7 -6
View File
@@ -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
+67 -37
View File
@@ -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 $<$<COMPILE_LANG_AND_ID:CXX,GNU>:-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")
+62 -7
View File
@@ -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=$<TARGET_PROPERTY:${COMPONENT_TARGET},INTERFACE_INCLUDE_DIRECTORIES>
-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()
@@ -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)