From b6208df4634dae6360051dc963c9acf6c2339bd9 Mon Sep 17 00:00:00 2001 From: Marius Vikhammer Date: Fri, 5 Jul 2024 14:08:15 +0800 Subject: [PATCH] feat(lp-core): allow custom cmakefile project file for ULP project --- components/ulp/cmake/CMakeLists.txt | 201 +---------------- components/ulp/cmake/IDFULPProject.cmake | 211 ++++++++++++++++++ .../ulp/cmake/toolchain-esp32-ulp.cmake | 1 + .../ulp/cmake/toolchain-esp32s2-ulp.cmake | 1 + .../ulp/cmake/toolchain-esp32s3-ulp.cmake | 1 + components/ulp/project_include.cmake | 15 +- docs/en/api-reference/system/ulp-lp-core.rst | 60 ++++- docs/en/api-reference/system/ulp-risc-v.rst | 92 ++++++-- examples/system/.build-test-rules.yml | 7 +- .../ulp/lp_core/build_system/CMakeLists.txt | 6 + .../system/ulp/lp_core/build_system/README.md | 23 ++ .../lp_core/build_system/main/CMakeLists.txt | 8 + .../main/lp_core_build_system_example_main.c | 59 +++++ .../build_system/main/ulp/CMakeLists.txt | 31 +++ .../build_system/main/ulp/lib/lib_src.c | 9 + .../build_system/main/ulp/lib/lib_src.h | 8 + .../ulp/lp_core/build_system/main/ulp/main.c | 15 ++ .../build_system/pytest_lp_core_build_sys.py | 12 + .../lp_core/build_system/sdkconfig.defaults | 9 + 19 files changed, 551 insertions(+), 218 deletions(-) create mode 100644 components/ulp/cmake/IDFULPProject.cmake create mode 100644 examples/system/ulp/lp_core/build_system/CMakeLists.txt create mode 100644 examples/system/ulp/lp_core/build_system/README.md create mode 100644 examples/system/ulp/lp_core/build_system/main/CMakeLists.txt create mode 100644 examples/system/ulp/lp_core/build_system/main/lp_core_build_system_example_main.c create mode 100644 examples/system/ulp/lp_core/build_system/main/ulp/CMakeLists.txt create mode 100644 examples/system/ulp/lp_core/build_system/main/ulp/lib/lib_src.c create mode 100644 examples/system/ulp/lp_core/build_system/main/ulp/lib/lib_src.h create mode 100644 examples/system/ulp/lp_core/build_system/main/ulp/main.c create mode 100644 examples/system/ulp/lp_core/build_system/pytest_lp_core_build_sys.py create mode 100644 examples/system/ulp/lp_core/build_system/sdkconfig.defaults diff --git a/components/ulp/cmake/CMakeLists.txt b/components/ulp/cmake/CMakeLists.txt index 20db7cdb3f1..1da07bd0b5f 100644 --- a/components/ulp/cmake/CMakeLists.txt +++ b/components/ulp/cmake/CMakeLists.txt @@ -1,203 +1,12 @@ cmake_minimum_required(VERSION 3.16) include(${IDF_PATH}/tools/cmake/idf.cmake) -project(${ULP_APP_NAME} ASM C) +project(${ULP_APP_NAME}) add_executable(${ULP_APP_NAME}) set(CMAKE_EXECUTABLE_SUFFIX ".elf") -# Import all sdkconfig variables into the cmake build -include(${SDKCONFIG_CMAKE}) +include(IDFULPProject) -function(create_arg_file arguments output_file) - # Escape all spaces - list(TRANSFORM arguments REPLACE " " "\\\\ ") - # Create a single string with all args separated by space - list(JOIN arguments " " arguments) - # Write it into the response file - file(WRITE ${output_file} ${arguments}) -endfunction() - -message(STATUS "Building ULP app ${ULP_APP_NAME}") - -# Check the supported assembler version -if(CONFIG_ULP_COPROC_TYPE_FSM) - check_expected_tool_version("esp32ulp-elf" ${CMAKE_ASM_COMPILER}) -endif() - - -set(ULP_MAP_GEN ${PYTHON} ${IDF_PATH}/components/ulp/esp32ulp_mapgen.py) -get_filename_component(sdkconfig_dir ${SDKCONFIG_HEADER} DIRECTORY) - - - -foreach(include ${COMPONENT_INCLUDES}) - list(APPEND component_includes -I${include}) -endforeach() -list(REMOVE_DUPLICATES component_includes) - -list(APPEND ULP_PREPROCESSOR_ARGS ${component_includes}) -list(APPEND ULP_PREPROCESSOR_ARGS -I${COMPONENT_DIR}) -list(APPEND ULP_PREPROCESSOR_ARGS -I${sdkconfig_dir}) -list(APPEND ULP_PREPROCESSOR_ARGS -I${IDF_PATH}/components/esp_system/ld) - -target_include_directories(${ULP_APP_NAME} PRIVATE ${COMPONENT_INCLUDES}) - -# Pre-process the linker script -if(CONFIG_ULP_COPROC_TYPE_RISCV) - set(ULP_LD_TEMPLATE ${IDF_PATH}/components/ulp/ld/ulp_riscv.ld) -elseif(CONFIG_ULP_COPROC_TYPE_LP_CORE) - set(ULP_LD_TEMPLATE ${IDF_PATH}/components/ulp/ld/lp_core_riscv.ld) -else() - set(ULP_LD_TEMPLATE ${IDF_PATH}/components/ulp/ld/ulp_fsm.ld) -endif() - -get_filename_component(ULP_LD_SCRIPT ${ULP_LD_TEMPLATE} NAME) - -# Put all arguments to the list -set(preprocessor_args -D__ASSEMBLER__ -E -P -xc -o ${ULP_LD_SCRIPT} ${ULP_PREPROCESSOR_ARGS} ${ULP_LD_TEMPLATE}) -set(compiler_arguments_file ${CMAKE_CURRENT_BINARY_DIR}/${ULP_LD_SCRIPT}_args.txt) -create_arg_file("${preprocessor_args}" "${compiler_arguments_file}") - -add_custom_command(OUTPUT ${ULP_LD_SCRIPT} - COMMAND ${CMAKE_C_COMPILER} @${compiler_arguments_file} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - MAIN_DEPENDENCY ${ULP_LD_TEMPLATE} - DEPENDS ${SDKCONFIG_HEADER} - COMMENT "Generating ${ULP_LD_SCRIPT} linker script..." - VERBATIM) -add_custom_target(ld_script DEPENDS ${ULP_LD_SCRIPT}) -add_dependencies(${ULP_APP_NAME} ld_script) -target_link_options(${ULP_APP_NAME} PRIVATE SHELL:-T ${CMAKE_CURRENT_BINARY_DIR}/${ULP_LD_SCRIPT}) - -# To avoid warning "Manually-specified variables were not used by the project" -set(bypassWarning "${IDF_TARGET}") -if(CONFIG_ULP_COPROC_TYPE_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" - "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/start.S" - "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_adc.c" - "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_lock.c" - "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_uart.c" - "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_print.c" - "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_i2c.c" - "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_utils.c" - "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_touch.c" - "${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_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,-Map=${CMAKE_CURRENT_BINARY_DIR}/${ULP_APP_NAME}.map) - 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" - "${IDF_PATH}/components/ulp/ulp_riscv/shared/include" - "${IDF_PATH}/components/riscv/include") - target_link_options(${ULP_APP_NAME} PRIVATE SHELL:-T ${IDF_PATH}/components/ulp/ld/${IDF_TARGET}.peripherals.ld) - target_link_options(${ULP_APP_NAME} PRIVATE "-Wl,--no-warn-rwx-segments") - 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) - 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" - "${IDF_PATH}/components/ulp/lp_core/lp_core/port/${IDF_TARGET}/vector_table.S" - "${IDF_PATH}/components/ulp/lp_core/shared/ulp_lp_core_memory_shared.c" - "${IDF_PATH}/components/ulp/lp_core/shared/ulp_lp_core_lp_timer_shared.c" - "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_startup.c" - "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_utils.c" - - "${IDF_PATH}/components/hal/uart_hal_iram.c" - "${IDF_PATH}/components/hal/uart_hal.c" - "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_uart.c" - "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_print.c" - "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_panic.c" - "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_interrupt.c" - "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_i2c.c" - "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_spi.c") - - 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) - - set(target_folder ${IDF_TARGET}) - - target_link_options(${ULP_APP_NAME} - PRIVATE SHELL:-T ${IDF_PATH}/components/soc/${target_folder}/ld/${IDF_TARGET}.peripherals.ld) - - if(CONFIG_ESP_ROM_HAS_LP_ROM) - target_link_options(${ULP_APP_NAME} - PRIVATE SHELL:-T ${IDF_PATH}/components/esp_rom/${IDF_TARGET}/ld/${IDF_TARGET}lp.rom.ld) - target_link_options(${ULP_APP_NAME} - PRIVATE SHELL:-T ${IDF_PATH}/components/esp_rom/${IDF_TARGET}/ld/${IDF_TARGET}lp.rom.newlib.ld) - target_link_options(${ULP_APP_NAME} - PRIVATE SHELL:-T ${IDF_PATH}/components/esp_rom/${IDF_TARGET}/ld/${IDF_TARGET}lp.rom.version.ld) - target_link_options(${ULP_APP_NAME} - PRIVATE SHELL:-T ${IDF_PATH}/components/esp_rom/${IDF_TARGET}/ld/${IDF_TARGET}lp.rom.api.ld) - endif() - - target_sources(${ULP_APP_NAME} PRIVATE ${ULP_S_SOURCES}) - target_include_directories(${ULP_APP_NAME} PRIVATE "${IDF_PATH}/components/ulp/lp_core/lp_core/include" - "${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_PREPROCESSOR_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_link_options(${ULP_APP_NAME} PRIVATE -Map=${CMAKE_CURRENT_BINARY_DIR}/${ULP_APP_NAME}.map) - target_sources(${ULP_APP_NAME} PRIVATE ${ULP_PS_SOURCES}) - -endif() - -if(CONFIG_ULP_COPROC_TYPE_LP_CORE) - set(ULP_BASE_ADDR "0x0") -else() - set(ULP_BASE_ADDR "0x50000000") -endif() - -# Dump the list of global symbols in a convenient format -add_custom_command(OUTPUT ${ULP_APP_NAME}.sym - COMMAND ${CMAKE_NM} -f posix -g $ > ${ULP_APP_NAME}.sym - DEPENDS ${ULP_APP_NAME} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - -# Dump the binary for inclusion into the project -add_custom_command(OUTPUT ${ULP_APP_NAME}.bin - COMMAND ${CMAKE_OBJCOPY} -O binary $ ${ULP_APP_NAME}.bin - DEPENDS ${ULP_APP_NAME} - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - -add_custom_command(OUTPUT ${ULP_APP_NAME}.ld ${ULP_APP_NAME}.h - COMMAND ${ULP_MAP_GEN} -s ${ULP_APP_NAME}.sym -o ${ULP_APP_NAME} --base ${ULP_BASE_ADDR} - DEPENDS ${ULP_APP_NAME}.sym - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) - -# Building the component separately from the project should result in -# ULP files being built. -add_custom_target(build - DEPENDS ${ULP_APP_NAME} ${ULP_APP_NAME}.bin ${ULP_APP_NAME}.sym - ${CMAKE_CURRENT_BINARY_DIR}/${ULP_APP_NAME}.ld - ${CMAKE_CURRENT_BINARY_DIR}/${ULP_APP_NAME}.h - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) +ulp_apply_default_options(${ULP_APP_NAME}) +ulp_apply_default_sources(${ULP_APP_NAME}) +ulp_add_build_binary_targets(${ULP_APP_NAME}) diff --git a/components/ulp/cmake/IDFULPProject.cmake b/components/ulp/cmake/IDFULPProject.cmake new file mode 100644 index 00000000000..13614ba8449 --- /dev/null +++ b/components/ulp/cmake/IDFULPProject.cmake @@ -0,0 +1,211 @@ +include(${SDKCONFIG_CMAKE}) +enable_language(C ASM) + +# Check the supported assembler version +if(CONFIG_ULP_COPROC_TYPE_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) + 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) + 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() + target_link_options(${ulp_app_name} PRIVATE -Map=${CMAKE_CURRENT_BINARY_DIR}/${ulp_app_name}.map) + endif() +endfunction() + +function(ulp_apply_default_sources ulp_app_name) + + function(create_arg_file arguments output_file) + # Escape all spaces + list(TRANSFORM arguments REPLACE " " "\\\\ ") + # Create a single string with all args separated by space + list(JOIN arguments " " arguments) + # Write it into the response file + file(WRITE ${output_file} ${arguments}) + endfunction() + + message(STATUS "Building ULP app ${ulp_app_name}") + + get_filename_component(sdkconfig_dir ${SDKCONFIG_HEADER} DIRECTORY) + + foreach(include ${COMPONENT_INCLUDES}) + list(APPEND component_includes -I${include}) + endforeach() + list(REMOVE_DUPLICATES component_includes) + + list(APPEND ULP_PREPRO_ARGS ${component_includes}) + list(APPEND ULP_PREPRO_ARGS -I${COMPONENT_DIR}) + list(APPEND ULP_PREPRO_ARGS -I${sdkconfig_dir}) + list(APPEND ULP_PREPRO_ARGS -I${IDF_PATH}/components/esp_system/ld) + + target_include_directories(${ulp_app_name} PRIVATE ${COMPONENT_INCLUDES}) + + # Pre-process the linker script + if(CONFIG_ULP_COPROC_TYPE_RISCV) + set(ULP_LD_TEMPLATE ${IDF_PATH}/components/ulp/ld/ulp_riscv.ld) + elseif(CONFIG_ULP_COPROC_TYPE_LP_CORE) + set(ULP_LD_TEMPLATE ${IDF_PATH}/components/ulp/ld/lp_core_riscv.ld) + else() + set(ULP_LD_TEMPLATE ${IDF_PATH}/components/ulp/ld/ulp_fsm.ld) + endif() + + get_filename_component(ULP_LD_SCRIPT ${ULP_LD_TEMPLATE} NAME) + + # Put all arguments to the list + set(preprocessor_args -D__ASSEMBLER__ -E -P -xc -o ${ULP_LD_SCRIPT} ${ULP_PREPRO_ARGS} ${ULP_LD_TEMPLATE}) + set(compiler_arguments_file ${CMAKE_CURRENT_BINARY_DIR}/${ULP_LD_SCRIPT}_args.txt) + create_arg_file("${preprocessor_args}" "${compiler_arguments_file}") + + add_custom_command(OUTPUT ${ULP_LD_SCRIPT} + COMMAND ${CMAKE_C_COMPILER} @${compiler_arguments_file} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + MAIN_DEPENDENCY ${ULP_LD_TEMPLATE} + DEPENDS ${SDKCONFIG_HEADER} + COMMENT "Generating ${ULP_LD_SCRIPT} linker script..." + VERBATIM) + add_custom_target(ld_script DEPENDS ${ULP_LD_SCRIPT}) + add_dependencies(${ulp_app_name} ld_script) + target_link_options(${ulp_app_name} PRIVATE SHELL:-T ${CMAKE_CURRENT_BINARY_DIR}/${ULP_LD_SCRIPT}) + + # To avoid warning "Manually-specified variables were not used by the project" + set(bypassWarning "${IDF_TARGET}") + + if(CONFIG_ULP_COPROC_TYPE_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" + "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/start.S" + "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_adc.c" + "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_lock.c" + "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_uart.c" + "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_print.c" + "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_i2c.c" + "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_utils.c" + "${IDF_PATH}/components/ulp/ulp_riscv/ulp_core/ulp_riscv_touch.c" + "${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" + "${IDF_PATH}/components/ulp/ulp_riscv/shared/include" + "${IDF_PATH}/components/riscv/include") + target_link_options(${ulp_app_name} PRIVATE SHELL:-T ${IDF_PATH}/components/ulp/ld/${IDF_TARGET}.peripherals.ld) + 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) + 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" + "${IDF_PATH}/components/ulp/lp_core/lp_core/port/${IDF_TARGET}/vector_table.S" + "${IDF_PATH}/components/ulp/lp_core/shared/ulp_lp_core_memory_shared.c" + "${IDF_PATH}/components/ulp/lp_core/shared/ulp_lp_core_lp_timer_shared.c" + "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_startup.c" + "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_utils.c" + + "${IDF_PATH}/components/hal/uart_hal_iram.c" + "${IDF_PATH}/components/hal/uart_hal.c" + "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_uart.c" + "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_print.c" + "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_panic.c" + "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_interrupt.c" + "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_i2c.c" + "${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_spi.c") + + set(target_folder ${IDF_TARGET}) + + target_link_options(${ulp_app_name} + PRIVATE SHELL:-T ${IDF_PATH}/components/soc/${target_folder}/ld/${IDF_TARGET}.peripherals.ld) + + if(CONFIG_ESP_ROM_HAS_LP_ROM) + target_link_options(${ulp_app_name} + PRIVATE SHELL:-T ${IDF_PATH}/components/esp_rom/${IDF_TARGET}/ld/${IDF_TARGET}lp.rom.ld) + target_link_options(${ulp_app_name} + PRIVATE SHELL:-T ${IDF_PATH}/components/esp_rom/${IDF_TARGET}/ld/${IDF_TARGET}lp.rom.newlib.ld) + target_link_options(${ulp_app_name} + PRIVATE SHELL:-T ${IDF_PATH}/components/esp_rom/${IDF_TARGET}/ld/${IDF_TARGET}lp.rom.version.ld) + target_link_options(${ULP_APP_NAME} + PRIVATE SHELL:-T ${IDF_PATH}/components/esp_rom/${IDF_TARGET}/ld/${IDF_TARGET}lp.rom.api.ld) + endif() + + target_sources(${ulp_app_name} PRIVATE ${ULP_S_SOURCES}) + target_include_directories(${ulp_app_name} PRIVATE "${IDF_PATH}/components/ulp/lp_core/lp_core/include" + "${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() + +function(ulp_add_build_binary_targets ulp_app_name) + + if(CONFIG_ULP_COPROC_TYPE_LP_CORE) + set(ULP_BASE_ADDR "0x0") + else() + set(ULP_BASE_ADDR "0x50000000") + endif() + + set(ULP_MAP_GEN ${PYTHON} ${IDF_PATH}/components/ulp/esp32ulp_mapgen.py) + + # Dump the list of global symbols in a convenient format + add_custom_command(OUTPUT ${ULP_APP_NAME}.sym + COMMAND ${CMAKE_NM} -f posix -g $ > ${ulp_app_name}.sym + DEPENDS ${ulp_app_name} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + + # Dump the binary for inclusion into the project + add_custom_command(OUTPUT ${ulp_app_name}.bin + COMMAND ${CMAKE_OBJCOPY} -O binary $ ${ulp_app_name}.bin + DEPENDS ${ulp_app_name} + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + + add_custom_command(OUTPUT ${ulp_app_name}.ld ${ulp_app_name}.h + COMMAND ${ULP_MAP_GEN} -s ${ulp_app_name}.sym -o ${ulp_app_name} --base ${ULP_BASE_ADDR} + DEPENDS ${ulp_app_name}.sym + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + + # Building the component separately from the project should result in + # ULP files being built. + add_custom_target(build + DEPENDS ${ulp_app_name} ${ulp_app_name}.bin ${ulp_app_name}.sym + ${CMAKE_CURRENT_BINARY_DIR}/${ulp_app_name}.ld + ${CMAKE_CURRENT_BINARY_DIR}/${ulp_app_name}.h + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}) + +endfunction() diff --git a/components/ulp/cmake/toolchain-esp32-ulp.cmake b/components/ulp/cmake/toolchain-esp32-ulp.cmake index 401de56062e..599e939addc 100644 --- a/components/ulp/cmake/toolchain-esp32-ulp.cmake +++ b/components/ulp/cmake/toolchain-esp32-ulp.cmake @@ -6,6 +6,7 @@ set(CMAKE_C_COMPILER "xtensa-esp32-elf-gcc") set(CMAKE_CXX_COMPILER "xtensa-esp32-elf-g++") set(CMAKE_ASM_COMPILER "esp32ulp-elf-as") +set(CMAKE_OBJCOPY "esp32ulp-elf-objcopy") set(CMAKE_LINKER "esp32ulp-elf-ld") diff --git a/components/ulp/cmake/toolchain-esp32s2-ulp.cmake b/components/ulp/cmake/toolchain-esp32s2-ulp.cmake index a2f81eb8324..7eedc02982d 100644 --- a/components/ulp/cmake/toolchain-esp32s2-ulp.cmake +++ b/components/ulp/cmake/toolchain-esp32s2-ulp.cmake @@ -6,6 +6,7 @@ set(CMAKE_C_COMPILER "xtensa-esp32s2-elf-gcc") set(CMAKE_CXX_COMPILER "xtensa-esp32s2-elf-g++") set(CMAKE_ASM_COMPILER "esp32ulp-elf-as") +set(CMAKE_OBJCOPY "esp32ulp-elf-objcopy") set(CMAKE_LINKER "esp32ulp-elf-ld") set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OBJECT "${CMAKE_ASM${ASM_DIALECT}_COMPILER} \ diff --git a/components/ulp/cmake/toolchain-esp32s3-ulp.cmake b/components/ulp/cmake/toolchain-esp32s3-ulp.cmake index 514c32e6bdc..61e0a85c315 100644 --- a/components/ulp/cmake/toolchain-esp32s3-ulp.cmake +++ b/components/ulp/cmake/toolchain-esp32s3-ulp.cmake @@ -6,6 +6,7 @@ set(CMAKE_C_COMPILER "xtensa-esp32s3-elf-gcc") set(CMAKE_CXX_COMPILER "xtensa-esp32s3-elf-g++") set(CMAKE_ASM_COMPILER "esp32ulp-elf-as") +set(CMAKE_OBJCOPY "esp32ulp-elf-objcopy") set(CMAKE_LINKER "esp32ulp-elf-ld") set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OBJECT "${CMAKE_ASM${ASM_DIALECT}_COMPILER} \ diff --git a/components/ulp/project_include.cmake b/components/ulp/project_include.cmake index 7585854d028..d5532088958 100644 --- a/components/ulp/project_include.cmake +++ b/components/ulp/project_include.cmake @@ -1,7 +1,9 @@ # ulp_embed_binary # # Create ULP binary and embed into the application. -function(ulp_embed_binary app_name s_sources exp_dep_srcs) + +function(__setup_ulp_project app_name project_path s_sources exp_dep_srcs) + if(NOT CMAKE_BUILD_EARLY_EXPANSION) spaces2list(s_sources) foreach(source ${s_sources}) @@ -50,7 +52,7 @@ function(ulp_embed_binary app_name s_sources exp_dep_srcs) endif() externalproject_add(${app_name} - SOURCE_DIR ${idf_path}/components/ulp/cmake + SOURCE_DIR ${project_path} BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}/${app_name} INSTALL_COMMAND "" CMAKE_ARGS -DCMAKE_EXPORT_COMPILE_COMMANDS=ON @@ -65,6 +67,7 @@ function(ulp_embed_binary app_name s_sources exp_dep_srcs) -DSDKCONFIG_HEADER=${SDKCONFIG_HEADER} -DSDKCONFIG_CMAKE=${SDKCONFIG_CMAKE} -DPYTHON=${python} + -DCMAKE_MODULE_PATH=$ENV{IDF_PATH}/components/ulp/cmake/ ${extra_cmake_args} BUILD_COMMAND ${CMAKE_COMMAND} --build ${CMAKE_CURRENT_BINARY_DIR}/${app_name} --target build BUILD_BYPRODUCTS ${ulp_artifacts} ${ulp_artifacts_extras} ${ulp_ps_sources} @@ -87,3 +90,11 @@ function(ulp_embed_binary app_name s_sources exp_dep_srcs) target_add_binary_data(${COMPONENT_LIB} ${CMAKE_CURRENT_BINARY_DIR}/${app_name}/${app_name}.bin BINARY) endif() endfunction() + +function(ulp_embed_binary app_name s_sources exp_dep_srcs) + __setup_ulp_project("${app_name}" "${idf_path}/components/ulp/cmake" "${s_sources}" "${exp_dep_srcs}") +endfunction() + +function(ulp_add_project app_name project_path) + __setup_ulp_project("${app_name}" "${project_path}" "" "") +endfunction() diff --git a/docs/en/api-reference/system/ulp-lp-core.rst b/docs/en/api-reference/system/ulp-lp-core.rst index dd3cb7cd0c7..8dbad305605 100644 --- a/docs/en/api-reference/system/ulp-lp-core.rst +++ b/docs/en/api-reference/system/ulp-lp-core.rst @@ -16,7 +16,10 @@ The ULP LP-Core coprocessor has the following features: Compiling Code for the ULP LP-Core ---------------------------------- -The ULP LP-Core code is compiled together with your ESP-IDF project as a separate binary and automatically embedded into the main project binary. To achieve this do the following: +The ULP LP-Core code is compiled together with your ESP-IDF project as a separate binary and automatically embedded into the main project binary. There are two ways to achieve this: + +Using ``ulp_embed_binary`` +~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1. Place the ULP LP-Core code, written in C or assembly (with the ``.S`` extension), in a dedicated directory within the component directory, such as ``ulp/``. @@ -34,6 +37,59 @@ The ULP LP-Core code is compiled together with your ESP-IDF project as a separat 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. + +Using a Custom CMake Project +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +It is also possible to create a custom CMake project for the LP-Core. This gives more control over the build process and allows you to set compile options, link external libraries and all other things that are possible with a regular CMake project. + +To do this add the ULP project as an external project in your component CMakeLists.txt file: + +.. code-block:: cmake + + ulp_add_project("ULP_APP_NAME" "${CMAKE_SOURCE_DIR}/PATH_TO_DIR_WITH_ULP_PROJECT_FILE/") + +Create a folder which contains your ULP project files and a CMakeLists.txt file, located at the path given to ``ulp_add_project``. The CMakeLists.txt file should look like this: + +.. code-block:: cmake + + cmake_minimum_required(VERSION 3.16) + + # Project/target name is passed from the main project to allow IDF to have a dependency on this target + # as well as embed the binary into the main app + project(${ULP_APP_NAME}) + add_executable(${ULP_APP_NAME} main.c) + + # Import the ULP project helper functions + include(IDFULPProject) + + # Apply default compile options + ulp_apply_default_options(${ULP_APP_NAME}) + + # Apply default sources provided by the IDF ULP component + ulp_apply_default_sources(${ULP_APP_NAME}) + + # Add targets for building the binary, as well as the linkerscript which exports ULP shared variables to the main app + ulp_add_build_binary_targets(${ULP_APP_NAME}) + + # Everything below this line is optional and can be used to customize the build process + + # Create a custom library + set(lib_path "${CMAKE_CURRENT_LIST_DIR}/lib") + add_library(custom_lib STATIC "${lib_path}/lib_src.c") + target_include_directories(custom_lib PUBLIC "${lib_path}/") + + # Link the library + target_link_libraries(${ULP_APP_NAME} PRIVATE custom_lib) + + # Set custom compile flags + target_compile_options(${ULP_APP_NAME} PRIVATE -msave-restore) + +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. 2. Build the application as usual (e.g., ``idf.py app``). @@ -54,6 +110,7 @@ During the build process, the following steps are taken to build ULP program: 7. **Add the generated binary to the list of binary files** to be embedded into the application. + .. _ulp-lp-core-access-variables: Accessing the ULP LP-Core Program Variables @@ -232,6 +289,7 @@ Application Examples * :example:`system/ulp/lp_core/lp_uart/lp_uart_print` shows how to print various statements from a program running on the LP core. * :example:`system/ulp/lp_core/interrupt` shows how to register an interrupt handler on the LP core to receive an interrupt triggered by the main CPU. * :example:`system/ulp/lp_core/gpio_intr_pulse_counter` shows how to use GPIO interrupts to count pulses while the main CPU is in Deep-sleep mode. +* :example:`system/ulp/lp_core/build_system/` demonstrates how to include custom CMakeLists.txt file for the ULP app. API Reference ------------- diff --git a/docs/en/api-reference/system/ulp-risc-v.rst b/docs/en/api-reference/system/ulp-risc-v.rst index c205f11bac1..921c8cadb09 100644 --- a/docs/en/api-reference/system/ulp-risc-v.rst +++ b/docs/en/api-reference/system/ulp-risc-v.rst @@ -16,20 +16,20 @@ If you have already set up ESP-IDF with CMake build system according to the :doc In earlier versions of ESP-IDF, RISC-V toolchain had a different prefix: ``riscv-none-embed-gcc``. -Compiling the ULP RISC-V Code ------------------------------ +Compiling Code for the ULP RISC-V +---------------------------------- -To compile the ULP RISC-V code as part of the component, the following steps must be taken: +The ULP RISC-V code is compiled together with your ESP-IDF project as a separate binary and automatically embedded into the main project binary. There are two ways to achieve this: -1. The ULP RISC-V code, written in C or assembly (must use the ``.S`` extension), must be placed in a separate directory inside the component directory, for instance, ``ulp/``. +Using ``ulp_embed_binary`` +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -.. note:: +1. Place the ULP RISC-V code, written in C or assembly (with the ``.S`` extension), in a dedicated directory within the component directory, such as ``ulp/``. - When registering the component (via ``idf_component_register``), this directory should not be added to the ``SRC_DIRS`` argument as it is currently done for the ULP FSM. See the step below for how to properly add ULP source files. +2. After registering the component in the CMakeLists.txt file, call the ``ulp_embed_binary`` function. Here is an example: -2. Call ``ulp_embed_binary`` from the component CMakeLists.txt after registration. For example:: +.. code-block:: cmake - ... idf_component_register() set(ulp_app_name ulp_${COMPONENT_NAME}) @@ -38,25 +38,81 @@ To compile the ULP RISC-V code as part of the component, the following steps mus ulp_embed_binary(${ulp_app_name} "${ulp_sources}" "${ulp_exp_dep_srcs}") - The first argument to ``ulp_embed_binary`` specifies the ULP 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 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 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. + + +Using a Custom CMake Project +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +It is also possible to create a custom CMake project for the ULP RISC-V. This gives more control over the build process and allows you to set compile options, link external libraries and all other things that are possible with a regular CMake project. + +To do this add the ULP project as an external project in your component CMakeLists.txt file: + +.. code-block:: cmake + + ulp_add_project("ULP_APP_NAME" "${CMAKE_SOURCE_DIR}/PATH_TO_DIR_WITH_ULP_PROJECT_FILE/") + +Create a folder which contains your ULP project files and a CMakeLists.txt file, located at the path given to ``ulp_add_project``. The CMakeLists.txt file should look like this: + +.. code-block:: cmake + + cmake_minimum_required(VERSION 3.16) + + # Project/target name is passed from the main project to allow IDF to have a dependency on this target + # as well as embed the binary into the main app + project(${ULP_APP_NAME}) + add_executable(${ULP_APP_NAME} main.c) + + # Import the ULP project helper functions + include(IDFULPProject) + + # Apply default compile options + ulp_apply_default_options(${ULP_APP_NAME}) + + # Apply default sources provided by the IDF ULP component + ulp_apply_default_sources(${ULP_APP_NAME}) + + # Add targets for building the binary, as well as the linkerscript which exports ULP shared variables to the main app + ulp_add_build_binary_targets(${ULP_APP_NAME}) + + # Everything below this line is optional and can be used to customize the build process + + # Create a custom library + set(lib_path "${CMAKE_CURRENT_LIST_DIR}/lib") + add_library(custom_lib STATIC "${lib_path}/lib_src.c") + target_include_directories(custom_lib PUBLIC "${lib_path}/") + + # Link the library + target_link_libraries(${ULP_APP_NAME} PRIVATE custom_lib) + + # Set custom compile flags + target_compile_options(${ULP_APP_NAME} PRIVATE -msave-restore) + +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. + +2. Build the application as usual (e.g., ``idf.py app``). -3. Build the application as usual (e.g., ``idf.py app``). +During the build process, the following steps are taken to build ULP program: - Inside, the build system will take the following steps to build ULP program: + 1. **Run each source file through the C compiler and assembler.** This step generates the object files ``.obj.c`` or ``.obj.S`` in the component build directory depending on the source file processed. - 1. **Run each source file through the C compiler and assembler.** This step generates the object files (``.obj.c`` or ``.obj.S`` depending of source file processed) in the component build directory. + 2. **Run the linker script template through the C preprocessor.** The template is located in ``components/ulp/ld`` directory. - 2. **Run the linker script template through the C preprocessor.** The template is located in ``components/ulp/ld`` directory. + 3. **Link the object files into an output ELF file** (``ulp_app_name.elf``). The Map file ``ulp_app_name.map`` generated at this stage may be useful for debugging purposes. - 3. **Link the object files into an output ELF file** (``ulp_app_name.elf``). The Map file (``ulp_app_name.map``) generated at this stage may be useful for debugging purposes. + 4. **Dump the contents of the ELF file into a binary** (``ulp_app_name.bin``) which can then be embedded into the application. - 4. **Dump the contents of the ELF file into a binary** (``ulp_app_name.bin``) which can then be embedded into the application. + 5. **Generate a list of global symbols** (``ulp_app_name.sym``) in the ELF file using ``riscv32-esp-elf-nm``. - 5. **Generate a list of global symbols** (``ulp_app_name.sym``) in the ELF file using ``riscv32-esp-elf-nm``. + 6. **Create an LD export script and a header file** ``ulp_app_name.ld`` and ``ulp_app_name.h`` containing the symbols from ``ulp_app_name.sym``. This is done using the ``esp32ulp_mapgen.py`` utility. - 6. **Create an LD export script and a header file** (``ulp_app_name.ld`` and ``ulp_app_name.h``) containing the symbols from ``ulp_app_name.sym``. This is done using the ``esp32ulp_mapgen.py`` utility. + 7. **Add the generated binary to the list of binary files** to be embedded into the application. - 7. **Add the generated binary to the list of binary files** to be embedded into the application. .. _ulp-riscv-access-variables: diff --git a/examples/system/.build-test-rules.yml b/examples/system/.build-test-rules.yml index 7481a8b4c2b..b899aa677ad 100644 --- a/examples/system/.build-test-rules.yml +++ b/examples/system/.build-test-rules.yml @@ -273,12 +273,17 @@ examples/system/task_watchdog: depends_components: - esp_system +examples/system/ulp/lp_core/build_system: + enable: + - if: SOC_LP_CORE_SUPPORTED == 1 + depends_components: + - ulp + examples/system/ulp/lp_core/gpio: disable: - if: SOC_DEEP_SLEEP_SUPPORTED != 1 enable: - if: (SOC_LP_CORE_SUPPORTED == 1) and (SOC_RTCIO_PIN_COUNT > 0) - depends_components: - ulp diff --git a/examples/system/ulp/lp_core/build_system/CMakeLists.txt b/examples/system/ulp/lp_core/build_system/CMakeLists.txt new file mode 100644 index 00000000000..7bb7b68de63 --- /dev/null +++ b/examples/system/ulp/lp_core/build_system/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(lp_core_build_system_example) diff --git a/examples/system/ulp/lp_core/build_system/README.md b/examples/system/ulp/lp_core/build_system/README.md new file mode 100644 index 00000000000..cdb86741b94 --- /dev/null +++ b/examples/system/ulp/lp_core/build_system/README.md @@ -0,0 +1,23 @@ +| Supported Targets | ESP32-C5 | ESP32-C6 | ESP32-P4 | +| ----------------- | -------- | -------- | -------- | + +# LP Core Build System Custom Modification Example: + +This example demonstrates how to customize the build-process for a LP-Core project; + +The ULP project is added in the [component CMakeLists.txt](main/CMakeLists.txt) with `ulp_add_project()`, which lets you add the custom ULP project defined in [CmakeLists.txt](main/ulp/CMakeLists.txt) to the IDF build-process. + +This lets you customize the ULP app build process like you would any other standard cmake project. In this example we show how to add and link a custom library, as well as setting additional compile options. + +The ULP project defines and links a library with a test function, `lib_test_func_sum`, which simply adds two numbers. HP-Core will print the result of this operation as an indication that the ULP did indeed successfully link and run a function from this library. + +It also show how to add a custom compile option by applying `-msave-restore`, which will force the compiler to use a common library call for function prologues/epilogues, which may reduce the code-size. + +## Example output + +``` +... +Not a ULP wakeup, initializing it! +Sum calculated by ULP using external library func: 11 +... +``` \ No newline at end of file diff --git a/examples/system/ulp/lp_core/build_system/main/CMakeLists.txt b/examples/system/ulp/lp_core/build_system/main/CMakeLists.txt new file mode 100644 index 00000000000..7094783022a --- /dev/null +++ b/examples/system/ulp/lp_core/build_system/main/CMakeLists.txt @@ -0,0 +1,8 @@ +# Set usual component variables +set(app_sources "lp_core_build_system_example_main.c") + +idf_component_register(SRCS ${app_sources} + REQUIRES ulp + WHOLE_ARCHIVE) + +ulp_add_project("ulp_build_system_example" "${CMAKE_SOURCE_DIR}/main/ulp/") diff --git a/examples/system/ulp/lp_core/build_system/main/lp_core_build_system_example_main.c b/examples/system/ulp/lp_core/build_system/main/lp_core_build_system_example_main.c new file mode 100644 index 00000000000..349326f9125 --- /dev/null +++ b/examples/system/ulp/lp_core/build_system/main/lp_core_build_system_example_main.c @@ -0,0 +1,59 @@ +/* + * SPDX-FileCopyrightText: 2023-2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +/* LP core build system example + + This example code is in the Public Domain (or CC0 licensed, at your option.) + + Unless required by applicable law or agreed to in writing, this + software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR + CONDITIONS OF ANY KIND, either express or implied. +*/ + +#include +#include +#include "esp_sleep.h" +#include "ulp_lp_core.h" +#include "ulp_build_system_example.h" +#include "freertos/FreeRTOS.h" +#include "freertos/task.h" + +extern const uint8_t ulp_build_system_example_bin_start[] asm("_binary_ulp_build_system_example_bin_start"); +extern const uint8_t ulp_build_system_example_bin_end[] asm("_binary_ulp_build_system_example_bin_end"); + +static void init_ulp_program(void); + +void app_main(void) +{ + /* If user is using USB-serial-jtag then idf monitor needs some time to + * re-connect to the USB port. We wait 1 sec here to allow for it to make the reconnection + * before we print anything. Otherwise the chip will go back to sleep again before the user + * has time to monitor any output. + */ + vTaskDelay(pdMS_TO_TICKS(1000)); + + init_ulp_program(); + + while(ulp_sum == 0) { + // Wait for ULP to finish its boot up and calculate sum + } + printf("Sum calculated by ULP using external library func: %"PRIi32"\n", ulp_sum); + printf("Example finished\n"); + vTaskDelay(portMAX_DELAY); +} + +static void init_ulp_program(void) +{ + esp_err_t err = ulp_lp_core_load_binary(ulp_build_system_example_bin_start, (ulp_build_system_example_bin_end - ulp_build_system_example_bin_start)); + ESP_ERROR_CHECK(err); + + /* Start the program */ + ulp_lp_core_cfg_t cfg = { + .wakeup_source = ULP_LP_CORE_WAKEUP_SOURCE_HP_CPU, + }; + + err = ulp_lp_core_run(&cfg); + ESP_ERROR_CHECK(err); +} diff --git a/examples/system/ulp/lp_core/build_system/main/ulp/CMakeLists.txt b/examples/system/ulp/lp_core/build_system/main/ulp/CMakeLists.txt new file mode 100644 index 00000000000..5db624a7d74 --- /dev/null +++ b/examples/system/ulp/lp_core/build_system/main/ulp/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.16) + +# Project/target name is passed from the main project to allow IDF to have a dependency on this target +# as well as embed the binary into the main app +project(${ULP_APP_NAME}) +add_executable(${ULP_APP_NAME} main.c) + +# Import the ULP project helper functions +include(IDFULPProject) + +# Apply default compile options +ulp_apply_default_options(${ULP_APP_NAME}) + +# Apply default sources provided by the IDF ULP component +ulp_apply_default_sources(${ULP_APP_NAME}) + +# Add targets for building the binary, as well as the linkerscript which exports ULP shared variables to the main app +ulp_add_build_binary_targets(${ULP_APP_NAME}) + +# Everything below this line is optional and can be used to customize the build process + +# Create a custom library +set(lib_path "${CMAKE_CURRENT_LIST_DIR}/lib") +add_library(custom_lib STATIC "${lib_path}/lib_src.c") +target_include_directories(custom_lib PUBLIC "${lib_path}/") + +# Link the library +target_link_libraries(${ULP_APP_NAME} PRIVATE custom_lib) + +# Set custom compile flags +target_compile_options(${ULP_APP_NAME} PRIVATE -msave-restore) diff --git a/examples/system/ulp/lp_core/build_system/main/ulp/lib/lib_src.c b/examples/system/ulp/lp_core/build_system/main/ulp/lib/lib_src.c new file mode 100644 index 00000000000..d620c06893c --- /dev/null +++ b/examples/system/ulp/lp_core/build_system/main/ulp/lib/lib_src.c @@ -0,0 +1,9 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +int lib_test_func_sum(int a, int b) +{ + return a + b; +} diff --git a/examples/system/ulp/lp_core/build_system/main/ulp/lib/lib_src.h b/examples/system/ulp/lp_core/build_system/main/ulp/lib/lib_src.h new file mode 100644 index 00000000000..99f0121f4e6 --- /dev/null +++ b/examples/system/ulp/lp_core/build_system/main/ulp/lib/lib_src.h @@ -0,0 +1,8 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#pragma once + +int lib_test_func_sum(int a, int b); diff --git a/examples/system/ulp/lp_core/build_system/main/ulp/main.c b/examples/system/ulp/lp_core/build_system/main/ulp/main.c new file mode 100644 index 00000000000..47cbf9f0b5d --- /dev/null +++ b/examples/system/ulp/lp_core/build_system/main/ulp/main.c @@ -0,0 +1,15 @@ +/* + * SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD + * + * SPDX-License-Identifier: Unlicense OR CC0-1.0 + */ +#include "lib_src.h" + +int sum; + +int main (void) +{ + sum = lib_test_func_sum(5, 6); + /* ulp_lp_core_halt() is called automatically when main exits */ + return 0; +} diff --git a/examples/system/ulp/lp_core/build_system/pytest_lp_core_build_sys.py b/examples/system/ulp/lp_core/build_system/pytest_lp_core_build_sys.py new file mode 100644 index 00000000000..b4729143162 --- /dev/null +++ b/examples/system/ulp/lp_core/build_system/pytest_lp_core_build_sys.py @@ -0,0 +1,12 @@ +# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD +# SPDX-License-Identifier: CC0-1.0 +import pytest +from pytest_embedded_idf.dut import IdfDut + + +@pytest.mark.esp32c5 +@pytest.mark.esp32c6 +@pytest.mark.esp32p4 +@pytest.mark.generic +def test_lp_core_build_sys(dut: IdfDut) -> None: + dut.expect('Sum calculated by ULP using external library func: 11') diff --git a/examples/system/ulp/lp_core/build_system/sdkconfig.defaults b/examples/system/ulp/lp_core/build_system/sdkconfig.defaults new file mode 100644 index 00000000000..42aa360e61d --- /dev/null +++ b/examples/system/ulp/lp_core/build_system/sdkconfig.defaults @@ -0,0 +1,9 @@ +# Enable ULP +CONFIG_ULP_COPROC_ENABLED=y +CONFIG_ULP_COPROC_TYPE_LP_CORE=y +CONFIG_ULP_COPROC_RESERVE_MEM=4096 +# Set log level to Warning to produce clean output +CONFIG_BOOTLOADER_LOG_LEVEL_WARN=y +CONFIG_BOOTLOADER_LOG_LEVEL=2 +CONFIG_LOG_DEFAULT_LEVEL_WARN=y +CONFIG_LOG_DEFAULT_LEVEL=2