Files
esp-idf/tools/test_apps/system/ulp/full_subproject/fsm
Frantisek Hrbata a9ea4f1105 refactor(ulp): provide a ulp_project.cmake subproject wrapper API
Build ULP full subprojects through a dedicated entry file,
components/ulp/cmake/ulp_project.cmake, that wraps tools/cmakev2/idf.cmake
and layers a small ULP API on top, mirroring the cmakev2 layering:

  ulp_project_init      like idf_project_init (init, detect the ULP type,
                        reset the compile/link options inherited from the app)
  ulp_build_executable  like idf_build_executable, plus the embeddable
                        .bin/.h/.ld artifacts
  ulp_project_default   like idf_project_default (single-executable case)

A child project now includes this one file instead of idf.cmake and calls
these helpers directly, so idf_build_executable is used as-is for the
multi-binary case and the module-path indirection (include(IDFULPProject)
resolved via -DCMAKE_MODULE_PATH) is gone.

As a result:
- IDFULPProjectv2.cmake is removed; its setup moves into the wrapper.
- IDFULPProject.cmake becomes the CMake v1-only entry point.
- The ULP component no longer registers a POST_ELF callback; the binary
  artifacts are produced by ulp_build_executable.
- The v2 full-subproject examples (lp_core, riscv, fsm, multi_binary,
  combined) are updated to the new API.
- The ULP subproject API is documented in build-system-v2.rst.

Also fix a latent bug this exercises: idf_build_library emitted linker
scripts as "-T <name>" relying on a following "-L" search directory. GNU
ld only searches -L directories that precede -T, so the direct
esp32ulp-elf-ld link used for ULP FSM failed to open the script. Emit the
absolute path instead, matching what the CMake v1 ULP build already does.

Finally, replace the parent-argument bypass loop that used to live in
IDFULPProjectv2.cmake with --no-warn-unused-cli on the child configure,
and stop passing the unused IDF_PARENT_BUILD_DIR.

Signed-off-by: Frantisek Hrbata <frantisek.hrbata@espressif.com>
2026-07-15 09:34:05 +09:00
..

Supported Targets ESP32 ESP32-S2 ESP32-S3

ULP FSM Build System Example

This example demonstrates a separate ULP FSM project built with the CMake v2 ULP project helpers. It programs the ULP FSM coprocessor to increment a counter while the main CPUs are in deep sleep, then wake the main CPU after a fixed number of timer iterations.

ULP program written in assembly can be found across main/ulp/main/counter.S and main/ulp/main/wake_up.S (demonstrating multiple ULP source files). The build system assembles and links this program, converts it into binary format, and embeds it into the .rodata section of the ESP-IDF application.

At runtime, the main code running on the ESP (found in main.c) loads ULP program into the RTC_SLOW_MEM memory region using ulp_load_binary function. Main code configures the ULP program by setting up values of some variables and then starts it using ulp_run. Once the ULP program is started, it runs periodically, with the period set by the main program. The main program enables ULP wakeup source and puts the chip into deep sleep mode.

On each timer iteration, the ULP program increments iteration_count. Once this counter reaches wakeup_iteration (set by the main program), the ULP triggers wake up from deep sleep. Upon wakeup, the main program stops the ULP timer, prints the final counter value, and exits the example.

In this example, the CONFIG_BOOTLOADER_SKIP_VALIDATE_IN_DEEP_SLEEP Kconfig option is used, which allows you to reduce the boot time of the bootloader during waking up from deep sleep. The bootloader stores in rtc memory the address of a running partition and uses it when it wakes up. This example allows you to skip all image checks and speed up the boot.

Example output

Not ULP wakeup, initializing ULP
Entering deep sleep

ULP FSM woke up the main CPU
ULP FSM iteration count: 5
Example finished