mirror of
https://github.com/espressif/esp-idf.git
synced 2026-10-02 03:00:34 +03:00
refactor(ulp): make the ULP subproject directory explicit
components/ulp/cmake holds the project that builds a ULP program. Name it subproject, as the bootloader does.
This commit is contained in:
@@ -0,0 +1,16 @@
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
|
||||
if(IDF_BUILD_V2)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/CMakeLists_v2.txt)
|
||||
return()
|
||||
endif()
|
||||
|
||||
include(${IDF_PATH}/tools/cmake/idf.cmake)
|
||||
project(${ULP_APP_NAME})
|
||||
add_executable(${ULP_APP_NAME})
|
||||
|
||||
include(IDFULPProject)
|
||||
|
||||
ulp_apply_default_options(${ULP_APP_NAME})
|
||||
ulp_apply_default_sources(${ULP_APP_NAME})
|
||||
ulp_add_build_binary_targets(${ULP_APP_NAME} PREFIX ${ULP_VAR_PREFIX})
|
||||
@@ -0,0 +1,69 @@
|
||||
# CMake v2 default ULP project for ulp_embed_binary() callers. The ULP runtime
|
||||
# is built from the ulp component graph, and the legacy source list is attached
|
||||
# directly to the executable, matching the CMake v1 link semantics.
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/ulp_project.cmake)
|
||||
|
||||
if("${ULP_TYPE}" STREQUAL "fsm")
|
||||
project(${ULP_APP_NAME} ASM)
|
||||
else()
|
||||
project(${ULP_APP_NAME} C CXX ASM)
|
||||
endif()
|
||||
|
||||
ulp_project_init()
|
||||
|
||||
# A single executable is built, so DEFERRED optional-requires resolution is
|
||||
# safe and keeps the linked component set minimal.
|
||||
idf_build_set_property(IDF_COMPONENT_OPTIONAL_REQUIRES_MODE DEFERRED)
|
||||
|
||||
# CMake v1 linked the ulp_embed_binary() call-site sources before the ULP runtime.
|
||||
# In CMake v2 the runtime's WHOLE_ARCHIVE is emitted as a link option, before
|
||||
# executable objects, so attaching these sources directly would reverse that
|
||||
# order. Keep them in a separate archive that can be placed before the runtime,
|
||||
# preserving legacy code placement and strong-over-weak symbol resolution.
|
||||
function(__ulp_embed_binary_add_legacy_sources executable)
|
||||
set(legacy_sources_lib "${executable}_legacy_sources")
|
||||
add_library(${legacy_sources_lib} STATIC ${ULP_S_SOURCES})
|
||||
target_link_libraries(${legacy_sources_lib} PRIVATE "library_${executable}")
|
||||
if(ADD_PICOLIBC_SPECS)
|
||||
target_compile_options(${legacy_sources_lib} PRIVATE $<$<COMPILE_LANG_AND_ID:C,GNU>:-specs=picolibc.specs>)
|
||||
target_compile_options(${legacy_sources_lib} PRIVATE $<$<COMPILE_LANG_AND_ID:CXX,GNU>:-specs=picolibcpp.specs>)
|
||||
endif()
|
||||
|
||||
if(ULP_PARENT_SDKCONFIG_HEADER)
|
||||
# Legacy ulp_embed_binary() sources are authored against the parent app's
|
||||
# sdkconfig, including project-local Kconfig.projbuild symbols.
|
||||
get_filename_component(parent_sdkconfig_dir "${ULP_PARENT_SDKCONFIG_HEADER}" DIRECTORY)
|
||||
target_include_directories(${legacy_sources_lib} BEFORE PRIVATE "${parent_sdkconfig_dir}")
|
||||
endif()
|
||||
if(COMPONENT_INCLUDES)
|
||||
target_include_directories(${legacy_sources_lib} PRIVATE ${COMPONENT_INCLUDES})
|
||||
endif()
|
||||
# CMake v1 exposed COMPONENT_DIR only to the FSM assembly preprocessing, not to
|
||||
# the compilation of ULP C sources. The v2 FSM toolchain preprocesses inside
|
||||
# the compile rule, which takes its include flags from the target, so keep the
|
||||
# directory on the target for FSM builds only.
|
||||
if(BUILD_FSM AND COMPONENT_DIR)
|
||||
target_include_directories(${legacy_sources_lib} PRIVATE ${COMPONENT_DIR})
|
||||
endif()
|
||||
|
||||
__idf_build_link_whole_archive(${executable} PRIVATE ${legacy_sources_lib} BEFORE)
|
||||
endfunction()
|
||||
|
||||
set(executable_args COMPONENTS ulp)
|
||||
if(NOT BUILD_FSM)
|
||||
list(APPEND executable_args MAPFILE_TARGET ${ULP_APP_NAME}_mapfile)
|
||||
endif()
|
||||
ulp_build_executable(${ULP_APP_NAME} ${executable_args})
|
||||
__ulp_embed_binary_add_legacy_sources(${ULP_APP_NAME})
|
||||
|
||||
if(DEFINED ULP_VAR_PREFIX AND NOT "${ULP_VAR_PREFIX}" STREQUAL "")
|
||||
ulp_build_binary(${ULP_APP_NAME} PREFIX ${ULP_VAR_PREFIX})
|
||||
else()
|
||||
ulp_build_binary(${ULP_APP_NAME})
|
||||
endif()
|
||||
|
||||
idf_build_generate_metadata(EXECUTABLE ${ULP_APP_NAME})
|
||||
if(TARGET "${ULP_APP_NAME}_mapfile")
|
||||
idf_create_size_report("${ULP_APP_NAME}_mapfile" TARGET size)
|
||||
endif()
|
||||
idf_build_generate_depgraph("${ULP_APP_NAME}")
|
||||
@@ -0,0 +1,265 @@
|
||||
# This is the CMake v1 (legacy) ULP child entry point, used by ulp_embed_binary.
|
||||
# CMake v2 full-subproject builds include components/ulp/subproject/ulp_project.cmake
|
||||
# instead.
|
||||
#
|
||||
# Legacy ULP child projects are plain CMake projects, so the parent-provided
|
||||
# sdkconfig.cmake has to be loaded here before the common ULP helpers inspect
|
||||
# CONFIG_* values. CMake v1 callers pass this path explicitly.
|
||||
include(${SDKCONFIG_CMAKE})
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/IDFULPProjectCommon.cmake)
|
||||
enable_language(C ASM)
|
||||
set(CMAKE_EXECUTABLE_SUFFIX ".elf")
|
||||
|
||||
ulp_detect_build_type()
|
||||
ulp_apply_build_type_options()
|
||||
|
||||
# Check the supported assembler version
|
||||
if(BUILD_FSM)
|
||||
check_expected_tool_version("esp32ulp-elf" ${CMAKE_ASM_COMPILER})
|
||||
endif()
|
||||
|
||||
# CMake v1-only helpers that preprocess the ULP memory-layout linker template
|
||||
# with the C preprocessor. The CMake v2 path attaches the template through
|
||||
# target_linker_script(... FLAGS) and does not use these.
|
||||
function(__ulp_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)
|
||||
# Generate the response file late enough for target generator expressions
|
||||
# in include directories to resolve to their final build-system values.
|
||||
file(GENERATE OUTPUT "${output_file}" CONTENT "${arguments}")
|
||||
endfunction()
|
||||
|
||||
function(__ulp_add_preprocessed_linker_script ulp_app_name ld_template ld_script ld_script_target)
|
||||
# Use the C preprocessor so sdkconfig and SoC constants can shape the linker
|
||||
# template before the ULP executable is linked. -MD -MF -MT records every file
|
||||
# the preprocessor reads (sdkconfig.h, SoC headers, the base/layout/checks
|
||||
# parts and a custom LINKER_LAYOUT with its own includes) into a depfile, so
|
||||
# the script regenerates when any of them changes.
|
||||
set(ld_depfile ${CMAKE_CURRENT_BINARY_DIR}/${ld_script}.d)
|
||||
set(preprocessor_args -D__ASSEMBLER__ -E -P -xc -MD -MF ${ld_depfile} -MT ${ld_script}
|
||||
-o ${ld_script} ${ARGN} ${ld_template})
|
||||
set(compiler_arguments_file ${CMAKE_CURRENT_BINARY_DIR}/${ld_script}_args.txt)
|
||||
__ulp_create_arg_file("${preprocessor_args}" "${compiler_arguments_file}")
|
||||
|
||||
add_custom_command(OUTPUT ${ld_script}
|
||||
COMMAND ${CMAKE_C_COMPILER} @${compiler_arguments_file}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
|
||||
MAIN_DEPENDENCY ${ld_template}
|
||||
# The response file is a dependency too: a change visible only
|
||||
# in the preprocessor flags (e.g. a different LINKER layout
|
||||
# path) must regenerate the script even though no file recorded
|
||||
# in the depfile changed. __ulp_create_arg_file rewrites it
|
||||
# only when its content actually changes, so this does not
|
||||
# retrigger on every reconfigure.
|
||||
DEPENDS ${compiler_arguments_file}
|
||||
DEPFILE ${ld_depfile}
|
||||
COMMENT "Generating ${ld_script} linker script..."
|
||||
VERBATIM)
|
||||
|
||||
add_custom_target(${ld_script_target} DEPENDS ${ld_script})
|
||||
add_dependencies(${ulp_app_name} ${ld_script_target})
|
||||
target_link_options(${ulp_app_name} PRIVATE SHELL:-T ${CMAKE_CURRENT_BINARY_DIR}/${ld_script})
|
||||
set_property(TARGET ${ulp_app_name} APPEND PROPERTY LINK_DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/${ld_script})
|
||||
endfunction()
|
||||
|
||||
function(ulp_apply_default_options ulp_app_name)
|
||||
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(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)
|
||||
elseif(BUILD_FSM)
|
||||
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)
|
||||
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)
|
||||
list(APPEND ULP_PREPRO_ARGS -I${IDF_PATH}/components/esp_system/ld/${IDF_TARGET})
|
||||
|
||||
target_include_directories(${ulp_app_name} PRIVATE ${COMPONENT_INCLUDES} ${sdkconfig_dir})
|
||||
|
||||
# Pre-process the linker script. The LP-core script is assembled from
|
||||
# base + layout + checks. A user-supplied LINKER_LAYOUT (LP_CORE_LINKER_SCRIPT)
|
||||
# replaces the layout, swapped in by the wrapper. This is supported for the
|
||||
# LP-core type only.
|
||||
if(BUILD_RISCV)
|
||||
set(ULP_LD_TEMPLATE ${IDF_PATH}/components/ulp/ld/ulp_riscv.ld.in)
|
||||
elseif(BUILD_LP_CORE)
|
||||
set(ULP_LD_TEMPLATE ${IDF_PATH}/components/ulp/ld/lp_core_riscv.ld.in)
|
||||
elseif(BUILD_FSM)
|
||||
set(ULP_LD_TEMPLATE ${IDF_PATH}/components/ulp/ld/ulp_fsm.ld.in)
|
||||
else()
|
||||
message(FATAL_ERROR "Unable to determine ULP type. ")
|
||||
endif()
|
||||
|
||||
if(LP_CORE_LINKER_SCRIPT)
|
||||
# The LINKER_LAYOUT-is-LP-core-only check is enforced once in
|
||||
# ulp_embed_binary/ulp_add_project, so LP_CORE_LINKER_SCRIPT only ever
|
||||
# reaches an LP-core child.
|
||||
message(STATUS "Using custom LP-core linker layout: ${LP_CORE_LINKER_SCRIPT}")
|
||||
# The inner escaped quotes are required: the wrapper does
|
||||
# `#include LP_CORE_LINKER_INCLUDE`, so the macro must expand to a quoted
|
||||
# header-name token ("/abs/path.ld"). __ulp_create_arg_file() additionally
|
||||
# escapes spaces in the path, keeping it a single preprocessor token.
|
||||
list(APPEND ULP_PREPRO_ARGS "-DLP_CORE_LINKER_INCLUDE=\\\"${LP_CORE_LINKER_SCRIPT}\\\"")
|
||||
endif()
|
||||
|
||||
# Strip the .in suffix so the generated script keeps its .ld name.
|
||||
get_filename_component(ULP_LD_SCRIPT ${ULP_LD_TEMPLATE} NAME_WLE)
|
||||
__ulp_add_preprocessed_linker_script(${ulp_app_name} ${ULP_LD_TEMPLATE} ${ULP_LD_SCRIPT}
|
||||
ld_script ${ULP_PREPRO_ARGS})
|
||||
|
||||
# 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}")
|
||||
|
||||
# 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"
|
||||
"${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(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)
|
||||
__ulp_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"
|
||||
"${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_pmp.c"
|
||||
"${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_utils.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_ubsan.c"
|
||||
"${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_mailbox.c"
|
||||
"${IDF_PATH}/components/ulp/lp_core/shared/ulp_lp_core_lp_adc_shared.c"
|
||||
"${IDF_PATH}/components/ulp/lp_core/shared/ulp_lp_core_lp_vad_shared.c"
|
||||
"${IDF_PATH}/components/ulp/lp_core/shared/ulp_lp_core_critical_section_shared.c")
|
||||
|
||||
if(CONFIG_SOC_LP_CORE_SUPPORT_I2C)
|
||||
list(APPEND ULP_S_SOURCES
|
||||
"${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_i2c.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_LP_SPI_SUPPORTED)
|
||||
list(APPEND ULP_S_SOURCES
|
||||
"${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_spi.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_ULP_LP_UART_SUPPORTED)
|
||||
list(APPEND ULP_S_SOURCES
|
||||
"${IDF_PATH}/components/ulp/lp_core/shared/ulp_lp_core_lp_uart_shared.c"
|
||||
"${IDF_PATH}/components/esp_driver_uart/src/uart_wakeup.c"
|
||||
"${IDF_PATH}/components/esp_hal_uart/uart_hal_iram.c"
|
||||
"${IDF_PATH}/components/esp_hal_uart/uart_hal.c"
|
||||
"${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_uart.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_LP_MAILBOX_SUPPORTED)
|
||||
list(APPEND ULP_S_SOURCES
|
||||
"${IDF_PATH}/components/ulp/lp_core/lp_core/port/lp_core_mailbox_impl_hw.c")
|
||||
else()
|
||||
list(APPEND ULP_S_SOURCES
|
||||
"${IDF_PATH}/components/ulp/lp_core/lp_core/port/lp_core_mailbox_impl_sw.c")
|
||||
endif()
|
||||
|
||||
if(CONFIG_SOC_TOUCH_SENSOR_SUPPORTED)
|
||||
list(APPEND ULP_S_SOURCES
|
||||
"${IDF_PATH}/components/ulp/lp_core/lp_core/lp_core_touch.c")
|
||||
endif()
|
||||
|
||||
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)
|
||||
|
||||
endif()
|
||||
endfunction()
|
||||
@@ -0,0 +1,101 @@
|
||||
# The directory of this file, captured while it is being processed:
|
||||
# CMAKE_CURRENT_LIST_DIR inside a function resolves at call time, which is the
|
||||
# caller's directory, not this one.
|
||||
set(__ULP_SUBPROJECT_DIR "${CMAKE_CURRENT_LIST_DIR}")
|
||||
|
||||
macro(ulp_detect_build_type)
|
||||
# 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()
|
||||
endmacro()
|
||||
|
||||
macro(ulp_apply_build_type_options)
|
||||
# CMake initializes GNU ASM definitions as --defsym during enable_language().
|
||||
# Native full-subproject FSM sources are preprocessed by the toolchain rule,
|
||||
# so use C preprocessor definitions for generated ASM rules.
|
||||
if(BUILD_FSM AND ULP_PREPROCESS_FSM_ASM_IN_TOOLCHAIN)
|
||||
set(CMAKE_ASM_DEFINE_FLAG "-D")
|
||||
endif()
|
||||
endmacro()
|
||||
|
||||
function(ulp_add_build_binary_targets ulp_app_name)
|
||||
cmake_parse_arguments(ULP "" "PREFIX" "" ${ARGN})
|
||||
if(NOT ULP_PREFIX)
|
||||
set(ULP_PREFIX "ulp_")
|
||||
endif()
|
||||
|
||||
if(ADD_PICOLIBC_SPECS)
|
||||
target_compile_options(${ulp_app_name} PRIVATE $<$<COMPILE_LANG_AND_ID:C,GNU>:-specs=picolibc.specs>)
|
||||
target_compile_options(${ulp_app_name} PRIVATE $<$<COMPILE_LANG_AND_ID:CXX,GNU>:-specs=picolibcpp.specs>)
|
||||
endif()
|
||||
|
||||
if(BUILD_LP_CORE)
|
||||
set(ULP_BASE_ADDR "0x0")
|
||||
else()
|
||||
set(ULP_BASE_ADDR "0x50000000")
|
||||
endif()
|
||||
|
||||
set(ULP_MAP_GEN ${PYTHON} ${__ULP_SUBPROJECT_DIR}/esp32ulp_mapgen.py)
|
||||
|
||||
# Dump the list of global symbols in a convenient format
|
||||
add_custom_command(OUTPUT ${ulp_app_name}.sym
|
||||
COMMAND ${CMAKE_READELF} -sW $<TARGET_FILE:${ulp_app_name}> > ${ulp_app_name}.sym
|
||||
DEPENDS ${ulp_app_name}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
# Dump the binary for inclusion into the project
|
||||
if(BUILD_LP_CORE AND CONFIG_ULP_COPROC_RUN_FROM_HP_MEM)
|
||||
# LP-core images running from HP memory use the normal ESP image format;
|
||||
# RTC-memory ULP binaries are raw object-copy output.
|
||||
add_custom_command(OUTPUT ${ulp_app_name}.bin
|
||||
COMMAND ${PYTHON} -m esptool --chip ${IDF_TARGET} elf2image
|
||||
--output ${ulp_app_name}.bin $<TARGET_FILE:${ulp_app_name}>
|
||||
DEPENDS ${ulp_app_name}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
else()
|
||||
add_custom_command(OUTPUT ${ulp_app_name}.bin
|
||||
COMMAND ${CMAKE_OBJCOPY} -O binary $<TARGET_FILE:${ulp_app_name}> ${ulp_app_name}.bin
|
||||
DEPENDS ${ulp_app_name}
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
endif()
|
||||
|
||||
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} --prefix ${ULP_PREFIX}
|
||||
--target ${IDF_TARGET}
|
||||
DEPENDS ${ulp_app_name}.sym
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
|
||||
add_custom_target(${ulp_app_name}_binary_artifacts
|
||||
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})
|
||||
|
||||
# Building the component separately from the project should result in all
|
||||
# ULP files being built. Multiple ULP executables in one child project all
|
||||
# contribute their artifact targets here.
|
||||
if(NOT TARGET build)
|
||||
add_custom_target(build)
|
||||
endif()
|
||||
add_dependencies(build ${ulp_app_name}_binary_artifacts)
|
||||
endfunction()
|
||||
Executable
+166
@@ -0,0 +1,166 @@
|
||||
#!/usr/bin/env python
|
||||
# SPDX-FileCopyrightText: 2016-2026 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# esp32ulp_mapgen utility converts a symbol list provided by nm into an export script
|
||||
# for the linker and a header file.
|
||||
import argparse
|
||||
import os
|
||||
import re
|
||||
import textwrap
|
||||
import typing
|
||||
|
||||
UTIL = os.path.basename(__file__)
|
||||
|
||||
|
||||
def name_mangling(name: str) -> str:
|
||||
# Simple and dumb name mangling for namespaced name following GCC algorithm
|
||||
ns, n = name.split('::')
|
||||
return f'_ZN{len(ns)}{ns}{len(n)}{n}E'
|
||||
|
||||
|
||||
# On ESP32-P4 the LP CPU accesses HP SRAM via the L2 cache at the same
|
||||
# address as the HP CPU cacheable alias. The HP application should access
|
||||
# LP Core variables through the non-cacheable alias to avoid coherency
|
||||
# issues with the LP CPU. The non-cacheable alias is obtained by adding
|
||||
# the offset 0x40000000 to addresses below 0x50000000.
|
||||
_P4_NON_CACHEABLE_OFFSET = 0x40000000
|
||||
_P4_LP_RAM_BASE = 0x50000000
|
||||
|
||||
|
||||
def map_to_hp_cpu_addr(addr: int, target: str | None) -> int:
|
||||
"""Translate an LP-CPU VMA to the address the HP CPU should use to access it."""
|
||||
if target == 'esp32p4' and addr < _P4_LP_RAM_BASE:
|
||||
return addr + _P4_NON_CACHEABLE_OFFSET
|
||||
return addr
|
||||
|
||||
|
||||
def gen_ld_h_from_sym(
|
||||
f_sym: typing.TextIO,
|
||||
f_ld: typing.TextIO,
|
||||
f_h: typing.TextIO,
|
||||
base_addr: int,
|
||||
prefix: str,
|
||||
target: str | None = None,
|
||||
) -> None:
|
||||
f_ld.write(
|
||||
textwrap.dedent(
|
||||
f"""
|
||||
/* ULP variable definitions for the linker.
|
||||
* This file is generated automatically by {UTIL} utility.
|
||||
*/
|
||||
""" # noqa: E222
|
||||
)
|
||||
)
|
||||
cpp_mode = False
|
||||
var_prefix = prefix
|
||||
namespace = ''
|
||||
if '::' in prefix:
|
||||
# C++ mode, let's avoid the extern "C" type and instead use namespace
|
||||
f_h.write(
|
||||
textwrap.dedent(
|
||||
f"""
|
||||
/* ULP variable definitions for the compiler.
|
||||
* This file is generated automatically by {UTIL} utility.
|
||||
*/
|
||||
#pragma once
|
||||
""" # noqa: E222
|
||||
)
|
||||
)
|
||||
tmp = prefix.split('::')
|
||||
namespace = tmp[0]
|
||||
var_prefix = '_'.join(tmp[1:]) # Limit to a single namespace here to avoid complex mangling rules
|
||||
f_h.write(f'namespace {namespace} {{\n')
|
||||
cpp_mode = True
|
||||
else:
|
||||
f_h.write(
|
||||
textwrap.dedent(
|
||||
f"""
|
||||
/* ULP variable definitions for the compiler.
|
||||
* This file is generated automatically by {UTIL} utility.
|
||||
*/
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#ifdef __cplusplus
|
||||
extern "C" {{
|
||||
#endif\n
|
||||
""" # noqa: E222
|
||||
)
|
||||
)
|
||||
|
||||
# Format the regular expression to match the readelf output
|
||||
expr = re.compile(
|
||||
r'^.*(?P<address>[a-f0-9]{8})\s+(?P<size>\d+) (OBJECT|NOTYPE)\s+GLOBAL\s+DEFAULT\s+[^ ]+ (?P<name>.*)$'
|
||||
)
|
||||
for line in f_sym:
|
||||
# readelf format output has the following structure:
|
||||
# Num: Value Size Type Bind Vis Ndx Name
|
||||
# So match the line with a regular expression to parse it first
|
||||
groups = expr.match(line)
|
||||
|
||||
# Ignore lines that do not match the expected format such as non global symbols
|
||||
if groups is None:
|
||||
continue
|
||||
|
||||
# Extract the symbol information
|
||||
addr = map_to_hp_cpu_addr(int(groups.group('address'), 16) + base_addr, target)
|
||||
size = int(groups.group('size'))
|
||||
sym_name = groups.group('name')
|
||||
|
||||
# Add the variable prefix if provided
|
||||
var_name = var_prefix + sym_name
|
||||
|
||||
# Get the dimension of the variable if it is an array
|
||||
var_dimension = f'[{size // 4}]' if size > 4 else '' # Use // to automatically perform an integer division
|
||||
|
||||
# Write the variable definition to the header file and the address to the linker script
|
||||
f_h.write(f'extern uint32_t {var_name}{var_dimension};\n')
|
||||
|
||||
name_in_ld = name_mangling(namespace + '::' + var_name) if cpp_mode else var_name
|
||||
f_ld.write(f'{name_in_ld} = 0x{addr:08x};\n')
|
||||
|
||||
if cpp_mode:
|
||||
f_h.write('}\n')
|
||||
else:
|
||||
f_h.write(
|
||||
textwrap.dedent(
|
||||
"""
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
"""
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
def main() -> None:
|
||||
description = (
|
||||
'This application generates .h and .ld files for symbols defined in input file. '
|
||||
'The input symbols file can be generated using readelf utility like this: '
|
||||
'<PREFIX>readelf -sW <elf_file> > <symbols_file>'
|
||||
)
|
||||
|
||||
parser = argparse.ArgumentParser(description=description)
|
||||
parser.add_argument(
|
||||
'-s', '--symfile', required=True, help='symbols file name', metavar='SYMFILE', type=argparse.FileType('r')
|
||||
)
|
||||
parser.add_argument(
|
||||
'-o', '--outputfile', required=True, help='destination .h and .ld files name prefix', metavar='OUTFILE'
|
||||
)
|
||||
parser.add_argument('--base-addr', required=True, help='base address of the ULP memory, to be added to each symbol')
|
||||
parser.add_argument('-p', '--prefix', required=False, help='prefix for generated header file', default='ulp_')
|
||||
parser.add_argument(
|
||||
'--target',
|
||||
required=False,
|
||||
help='IDF target chip name (e.g. esp32p4), used for address translation',
|
||||
default=None,
|
||||
)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
with open(args.outputfile + '.h', 'w') as f_h, open(args.outputfile + '.ld', 'w') as f_ld:
|
||||
gen_ld_h_from_sym(args.symfile, f_ld, f_h, int(args.base_addr, 0), args.prefix, args.target)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -0,0 +1,49 @@
|
||||
# SPDX-FileCopyrightText: 2026 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
foreach(required_var ULP_FSM_PREPROCESSOR ULP_FSM_ASSEMBLER ULP_FSM_TARGET ULP_FSM_SOURCE ULP_FSM_OBJECT)
|
||||
if(NOT DEFINED ${required_var} OR "${${required_var}}" STREQUAL "")
|
||||
message(FATAL_ERROR "${required_var} is required")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(extra_args)
|
||||
set(extra_args_started OFF)
|
||||
math(EXPR last_arg_index "${CMAKE_ARGC} - 1")
|
||||
foreach(arg_index RANGE 0 ${last_arg_index})
|
||||
if("${CMAKE_ARGV${arg_index}}" STREQUAL "--")
|
||||
set(extra_args_started ON)
|
||||
continue()
|
||||
endif()
|
||||
|
||||
if(extra_args_started)
|
||||
list(APPEND extra_args "${CMAKE_ARGV${arg_index}}")
|
||||
endif()
|
||||
endforeach()
|
||||
|
||||
set(preprocessed_source "${ULP_FSM_OBJECT}.ulp.S")
|
||||
set(listing_file "${ULP_FSM_OBJECT}.lst")
|
||||
|
||||
execute_process(
|
||||
COMMAND "${ULP_FSM_PREPROCESSOR}"
|
||||
-D__ASSEMBLER__ -E -P -x c
|
||||
${extra_args}
|
||||
-o "${preprocessed_source}"
|
||||
"${ULP_FSM_SOURCE}"
|
||||
RESULT_VARIABLE preprocess_result
|
||||
)
|
||||
if(NOT preprocess_result EQUAL 0)
|
||||
message(FATAL_ERROR "Failed to preprocess ${ULP_FSM_SOURCE}")
|
||||
endif()
|
||||
|
||||
execute_process(
|
||||
COMMAND "${ULP_FSM_ASSEMBLER}"
|
||||
"--mcpu=${ULP_FSM_TARGET}"
|
||||
"-al=${listing_file}"
|
||||
-o "${ULP_FSM_OBJECT}"
|
||||
-c "${preprocessed_source}"
|
||||
RESULT_VARIABLE assemble_result
|
||||
)
|
||||
if(NOT assemble_result EQUAL 0)
|
||||
message(FATAL_ERROR "Failed to assemble ${preprocessed_source}")
|
||||
endif()
|
||||
@@ -0,0 +1,36 @@
|
||||
# CMake toolchain file for ULP
|
||||
set(CMAKE_SYSTEM_NAME Generic)
|
||||
|
||||
# Compiler is only used for preprocessing
|
||||
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")
|
||||
|
||||
# Native CMake v2 ULP projects include cmakev2/idf.cmake before project(),
|
||||
# making idf_project_init visible when this toolchain is loaded. Legacy child
|
||||
# projects, even when launched by a CMake v2 parent, include the old IDF CMake
|
||||
# entrypoint and keep the historical explicit preprocessing path.
|
||||
set(ULP_PREPROCESS_FSM_ASM_IN_TOOLCHAIN OFF)
|
||||
if(IDF_BUILD_V2 AND COMMAND idf_project_init)
|
||||
set(ULP_PREPROCESS_FSM_ASM_IN_TOOLCHAIN ON)
|
||||
endif()
|
||||
|
||||
if(ULP_PREPROCESS_FSM_ASM_IN_TOOLCHAIN)
|
||||
set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OBJECT
|
||||
"${CMAKE_COMMAND} \
|
||||
-DULP_FSM_PREPROCESSOR=${CMAKE_C_COMPILER} \
|
||||
-DULP_FSM_ASSEMBLER=${CMAKE_ASM${ASM_DIALECT}_COMPILER} \
|
||||
-DULP_FSM_TARGET=esp32 \
|
||||
-DULP_FSM_SOURCE=<SOURCE> \
|
||||
-DULP_FSM_OBJECT=<OBJECT> \
|
||||
-P ${CMAKE_CURRENT_LIST_DIR}/preprocess_fsm_asm.cmake -- <DEFINES> <INCLUDES>")
|
||||
else()
|
||||
set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OBJECT "${CMAKE_ASM${ASM_DIALECT}_COMPILER} \
|
||||
--mcpu=esp32 <DEFINES> <INCLUDES> -o <OBJECT> -c <SOURCE>")
|
||||
endif()
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-A elf32-esp32ulp -nostdlib" CACHE STRING "ULP Linker Base Flags")
|
||||
set(CMAKE_ASM_LINK_EXECUTABLE "${CMAKE_LINKER} <FLAGS> <CMAKE_ASM_LINK_FLAGS> \
|
||||
<LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
|
||||
@@ -0,0 +1,36 @@
|
||||
# CMake toolchain file for ULP
|
||||
set(CMAKE_SYSTEM_NAME Generic)
|
||||
|
||||
# Compiler is only used for preprocessing
|
||||
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")
|
||||
|
||||
# Native CMake v2 ULP projects include cmakev2/idf.cmake before project(),
|
||||
# making idf_project_init visible when this toolchain is loaded. Legacy child
|
||||
# projects, even when launched by a CMake v2 parent, include the old IDF CMake
|
||||
# entrypoint and keep the historical explicit preprocessing path.
|
||||
set(ULP_PREPROCESS_FSM_ASM_IN_TOOLCHAIN OFF)
|
||||
if(IDF_BUILD_V2 AND COMMAND idf_project_init)
|
||||
set(ULP_PREPROCESS_FSM_ASM_IN_TOOLCHAIN ON)
|
||||
endif()
|
||||
|
||||
if(ULP_PREPROCESS_FSM_ASM_IN_TOOLCHAIN)
|
||||
set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OBJECT
|
||||
"${CMAKE_COMMAND} \
|
||||
-DULP_FSM_PREPROCESSOR=${CMAKE_C_COMPILER} \
|
||||
-DULP_FSM_ASSEMBLER=${CMAKE_ASM${ASM_DIALECT}_COMPILER} \
|
||||
-DULP_FSM_TARGET=esp32s2 \
|
||||
-DULP_FSM_SOURCE=<SOURCE> \
|
||||
-DULP_FSM_OBJECT=<OBJECT> \
|
||||
-P ${CMAKE_CURRENT_LIST_DIR}/preprocess_fsm_asm.cmake -- <DEFINES> <INCLUDES>")
|
||||
else()
|
||||
set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OBJECT "${CMAKE_ASM${ASM_DIALECT}_COMPILER} \
|
||||
--mcpu=esp32s2 <DEFINES> <INCLUDES> -o <OBJECT> -c <SOURCE>")
|
||||
endif()
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-A elf32-esp32s2ulp -nostdlib" CACHE STRING "ULP Linker Base Flags")
|
||||
set(CMAKE_ASM_LINK_EXECUTABLE "${CMAKE_LINKER} <FLAGS> <CMAKE_ASM_LINK_FLAGS> \
|
||||
<LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
|
||||
@@ -0,0 +1,36 @@
|
||||
# CMake toolchain file for ULP
|
||||
set(CMAKE_SYSTEM_NAME Generic)
|
||||
|
||||
# Compiler is only used for preprocessing
|
||||
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")
|
||||
|
||||
# Native CMake v2 ULP projects include cmakev2/idf.cmake before project(),
|
||||
# making idf_project_init visible when this toolchain is loaded. Legacy child
|
||||
# projects, even when launched by a CMake v2 parent, include the old IDF CMake
|
||||
# entrypoint and keep the historical explicit preprocessing path.
|
||||
set(ULP_PREPROCESS_FSM_ASM_IN_TOOLCHAIN OFF)
|
||||
if(IDF_BUILD_V2 AND COMMAND idf_project_init)
|
||||
set(ULP_PREPROCESS_FSM_ASM_IN_TOOLCHAIN ON)
|
||||
endif()
|
||||
|
||||
if(ULP_PREPROCESS_FSM_ASM_IN_TOOLCHAIN)
|
||||
set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OBJECT
|
||||
"${CMAKE_COMMAND} \
|
||||
-DULP_FSM_PREPROCESSOR=${CMAKE_C_COMPILER} \
|
||||
-DULP_FSM_ASSEMBLER=${CMAKE_ASM${ASM_DIALECT}_COMPILER} \
|
||||
-DULP_FSM_TARGET=esp32s3 \
|
||||
-DULP_FSM_SOURCE=<SOURCE> \
|
||||
-DULP_FSM_OBJECT=<OBJECT> \
|
||||
-P ${CMAKE_CURRENT_LIST_DIR}/preprocess_fsm_asm.cmake -- <DEFINES> <INCLUDES>")
|
||||
else()
|
||||
set(CMAKE_ASM${ASM_DIALECT}_COMPILE_OBJECT "${CMAKE_ASM${ASM_DIALECT}_COMPILER} \
|
||||
--mcpu=esp32s3 <DEFINES> <INCLUDES> -o <OBJECT> -c <SOURCE>")
|
||||
endif()
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-A elf32-esp32s3ulp -nostdlib" CACHE STRING "ULP Linker Base Flags")
|
||||
set(CMAKE_ASM_LINK_EXECUTABLE "${CMAKE_LINKER} <FLAGS> <CMAKE_ASM_LINK_FLAGS> \
|
||||
<LINK_FLAGS> <OBJECTS> -o <TARGET> <LINK_LIBRARIES>")
|
||||
@@ -0,0 +1,15 @@
|
||||
# CMake toolchain file for ULP LP core
|
||||
set(CMAKE_SYSTEM_NAME Generic)
|
||||
|
||||
set(CMAKE_C_COMPILER "riscv32-esp-elf-gcc")
|
||||
set(CMAKE_CXX_COMPILER "riscv32-esp-elf-g++")
|
||||
set(CMAKE_ASM_COMPILER "riscv32-esp-elf-gcc")
|
||||
|
||||
set(CMAKE_C_FLAGS "-Os -ggdb -march=rv32imac_zicsr_zifencei_zaamo_zalrsc -mdiv -fdata-sections -ffunction-sections"
|
||||
CACHE STRING "C Compiler Base Flags")
|
||||
set(CMAKE_CXX_FLAGS "-Os -ggdb -march=rv32imac_zicsr_zifencei_zaamo_zalrsc -mdiv -fdata-sections -ffunction-sections"
|
||||
CACHE STRING "C++ Compiler Base Flags")
|
||||
set(CMAKE_ASM_FLAGS "-Os -ggdb -march=rv32imac_zicsr_zifencei_zaamo_zalrsc -x assembler-with-cpp"
|
||||
CACHE STRING "Assembler Base Flags")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-march=rv32imac_zicsr_zifencei_zaamo_zalrsc --specs=nano.specs --specs=nosys.specs"
|
||||
CACHE STRING "Linker Base Flags")
|
||||
@@ -0,0 +1,15 @@
|
||||
# CMake toolchain file for ULP-RISC-V
|
||||
set(CMAKE_SYSTEM_NAME Generic)
|
||||
|
||||
set(CMAKE_C_COMPILER "riscv32-esp-elf-gcc")
|
||||
set(CMAKE_CXX_COMPILER "riscv32-esp-elf-g++")
|
||||
set(CMAKE_ASM_COMPILER "riscv32-esp-elf-gcc")
|
||||
|
||||
set(CMAKE_C_FLAGS "-Os -march=rv32imc_zicsr_zifencei -mdiv -fdata-sections -ffunction-sections"
|
||||
CACHE STRING "C Compiler Base Flags")
|
||||
set(CMAKE_CXX_FLAGS "-Os -march=rv32imc_zicsr_zifencei -mdiv -fdata-sections -ffunction-sections"
|
||||
CACHE STRING "C++ Compiler Base Flags")
|
||||
set(CMAKE_ASM_FLAGS "-march=rv32imc -x assembler-with-cpp"
|
||||
CACHE STRING "Assembler Base Flags")
|
||||
set(CMAKE_EXE_LINKER_FLAGS "-march=rv32imc_zicsr_zifencei --specs=nano.specs --specs=nosys.specs"
|
||||
CACHE STRING "Linker Base Flags")
|
||||
@@ -0,0 +1,196 @@
|
||||
# ULP subproject entry point for CMake v2 full-subproject builds.
|
||||
#
|
||||
# A ULP child project includes this file instead of tools/cmakev2/idf.cmake
|
||||
# directly. It pulls in the cmakev2 build system and layers the ULP subproject
|
||||
# API on top: ulp_project_init, ulp_build_executable, ulp_build_binary and
|
||||
# ulp_project_default, the ULP analogs of idf_project_init,
|
||||
# idf_build_executable, idf_build_binary and idf_project_default.
|
||||
|
||||
include(${IDF_PATH}/tools/cmakev2/idf.cmake)
|
||||
include(${CMAKE_CURRENT_LIST_DIR}/IDFULPProjectCommon.cmake)
|
||||
|
||||
# __ULP_BUILDV2 is only set by the IDF_BUILD_V2 ULP child path for now.
|
||||
if(__ULP_BUILDV2)
|
||||
set(NON_OS_BUILD 1)
|
||||
# Clearing common components below only prevents automatic app-component closure.
|
||||
# Explicitly included ULP dependencies still read NON_OS_BUILD to select their no-OS shape.
|
||||
idf_build_set_property(NON_OS_BUILD 1)
|
||||
endif()
|
||||
|
||||
# Reset the compile/link state inherited from the parent app build and select
|
||||
# ULP-specific build behavior. Idempotent within a child configure.
|
||||
function(__ulp_prepare_build)
|
||||
idf_build_get_property(ulp_build_prepared __ULP_BUILD_PREPARED)
|
||||
if(ulp_build_prepared)
|
||||
return()
|
||||
endif()
|
||||
|
||||
if(BUILD_FSM)
|
||||
check_expected_tool_version("esp32ulp-elf" ${CMAKE_ASM_COMPILER})
|
||||
# FSM links with esp32ulp-elf-ld directly, not through GCC, so avoid
|
||||
# GCC-driver link grouping flags from the generic executable helper.
|
||||
idf_build_set_property(LINKER_TYPE ULP_FSM)
|
||||
endif()
|
||||
|
||||
# ULP projects use their own toolchain files and component-provided options,
|
||||
# so discard compile/link options added for normal app builds.
|
||||
foreach(property IN ITEMS
|
||||
COMPILE_OPTIONS
|
||||
C_COMPILE_OPTIONS
|
||||
CXX_COMPILE_OPTIONS
|
||||
ASM_COMPILE_OPTIONS
|
||||
COMPILE_DEFINITIONS
|
||||
LINK_OPTIONS)
|
||||
idf_build_set_property(${property} "")
|
||||
endforeach()
|
||||
|
||||
# Do not apply the default app component closure to ULP child projects.
|
||||
# ULP components should contribute dependencies explicitly.
|
||||
idf_build_set_property(__COMPONENT_REQUIRES_COMMON "")
|
||||
idf_build_set_property(__COMMON_COMPONENT_INTERFACES "")
|
||||
idf_build_set_property(__COMMON_COMPONENTS_INITIALIZED YES)
|
||||
|
||||
idf_build_set_property(__ULP_BUILD_PREPARED YES)
|
||||
endfunction()
|
||||
|
||||
#[[api
|
||||
.. cmakev2:macro:: ulp_project_init
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
ulp_project_init()
|
||||
|
||||
Initialize a ULP full-subproject build. Calls :cmakev2:ref:`idf_project_init`
|
||||
and then applies the ULP-specific setup: it detects the ULP type (RISC-V, LP
|
||||
core or FSM) from the ``ULP_TYPE`` variable or the project configuration and
|
||||
resets the compile and link options inherited from the parent application
|
||||
build, so the ULP executable uses only its own toolchain and
|
||||
component-provided options.
|
||||
|
||||
This is the ULP analog of :cmakev2:ref:`idf_project_init` and is the entry
|
||||
point for projects that build more than one ULP executable, followed by
|
||||
:cmakev2:ref:`ulp_build_executable` and :cmakev2:ref:`ulp_build_binary`
|
||||
calls. For the common single-executable case use
|
||||
:cmakev2:ref:`ulp_project_default` instead.
|
||||
#]]
|
||||
macro(ulp_project_init)
|
||||
idf_project_init()
|
||||
ulp_detect_build_type()
|
||||
ulp_apply_build_type_options()
|
||||
__ulp_prepare_build()
|
||||
endmacro()
|
||||
|
||||
#[[api
|
||||
.. cmakev2:function:: ulp_build_executable
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
ulp_build_executable(<executable>
|
||||
[COMPONENTS <component>...]
|
||||
[MAPFILE_TARGET <target>])
|
||||
|
||||
*executable[in]*
|
||||
|
||||
Name of the ULP executable target to create.
|
||||
|
||||
Build a ULP executable. This is the ULP analog of
|
||||
:cmakev2:ref:`idf_build_executable` and forwards its arguments to
|
||||
:cmakev2:ref:`idf_build_executable`. Call :cmakev2:ref:`ulp_build_binary`
|
||||
for each executable that should produce embeddable ULP artifacts.
|
||||
#]]
|
||||
function(ulp_build_executable executable)
|
||||
if("PREFIX" IN_LIST ARGN)
|
||||
message(FATAL_ERROR "Use ulp_build_binary(${executable} PREFIX ...) to set ULP symbol prefixes.")
|
||||
endif()
|
||||
|
||||
idf_build_executable(${executable} ${ARGN})
|
||||
endfunction()
|
||||
|
||||
#[[api
|
||||
.. cmakev2:function:: ulp_build_binary
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
ulp_build_binary(<executable>
|
||||
[PREFIX <prefix>])
|
||||
|
||||
*executable[in]*
|
||||
|
||||
ULP executable target for which to generate embeddable artifacts.
|
||||
|
||||
*PREFIX[in,opt]*
|
||||
|
||||
Symbol name prefix for the generated symbol header and linker export
|
||||
files. Defaults to ``ulp_``. Use a distinct prefix per executable when a
|
||||
project builds several ULP binaries.
|
||||
|
||||
Generate the ``.bin`` payload and symbol header/linker export files for a
|
||||
ULP executable. This is the ULP analog of :cmakev2:ref:`idf_build_binary`.
|
||||
#]]
|
||||
function(ulp_build_binary executable)
|
||||
cmake_parse_arguments(_ULP "" "PREFIX" "" ${ARGN})
|
||||
if(_ULP_UNPARSED_ARGUMENTS)
|
||||
message(FATAL_ERROR "Unexpected arguments to ulp_build_binary(): ${_ULP_UNPARSED_ARGUMENTS}")
|
||||
endif()
|
||||
|
||||
if(DEFINED _ULP_PREFIX)
|
||||
ulp_add_build_binary_targets(${executable} PREFIX "${_ULP_PREFIX}")
|
||||
elseif(ULP_PREFIX_APPEND_BIN_NAME)
|
||||
set(prefix "ulp_")
|
||||
if(DEFINED ULP_VAR_PREFIX)
|
||||
set(prefix "${ULP_VAR_PREFIX}")
|
||||
endif()
|
||||
string(MAKE_C_IDENTIFIER "${prefix}${executable}_" prefix)
|
||||
ulp_add_build_binary_targets(${executable} PREFIX "${prefix}")
|
||||
else()
|
||||
ulp_add_build_binary_targets("${executable}")
|
||||
endif()
|
||||
endfunction()
|
||||
|
||||
# Build the single default ULP executable from the main component.
|
||||
function(__ulp_project_default)
|
||||
set(ulp_app_name "${PROJECT_NAME}")
|
||||
|
||||
set(executable_args COMPONENTS main)
|
||||
set(binary_args)
|
||||
if(NOT BUILD_FSM)
|
||||
list(APPEND executable_args MAPFILE_TARGET ${ulp_app_name}_mapfile)
|
||||
endif()
|
||||
if(DEFINED ULP_VAR_PREFIX AND NOT "${ULP_VAR_PREFIX}" STREQUAL "")
|
||||
list(APPEND binary_args PREFIX ${ULP_VAR_PREFIX})
|
||||
endif()
|
||||
|
||||
ulp_build_executable(${ulp_app_name} ${executable_args})
|
||||
ulp_build_binary(${ulp_app_name} ${binary_args})
|
||||
|
||||
idf_build_generate_metadata(EXECUTABLE ${ulp_app_name})
|
||||
|
||||
if(TARGET "${ulp_app_name}_mapfile")
|
||||
idf_create_size_report("${ulp_app_name}_mapfile" TARGET size)
|
||||
endif()
|
||||
|
||||
idf_build_generate_depgraph("${ulp_app_name}")
|
||||
endfunction()
|
||||
|
||||
#[[api
|
||||
.. cmakev2:macro:: ulp_project_default
|
||||
|
||||
.. code-block:: cmake
|
||||
|
||||
ulp_project_default()
|
||||
|
||||
Build a single ULP executable from the ``main`` component and its transitive
|
||||
dependencies, the ULP analog of :cmakev2:ref:`idf_project_default`. Calls
|
||||
:cmakev2:ref:`ulp_project_init`, then builds the executable (named after
|
||||
``PROJECT_NAME``) with :cmakev2:ref:`ulp_build_executable` and generates its
|
||||
embeddable artifacts with :cmakev2:ref:`ulp_build_binary`.
|
||||
#]]
|
||||
macro(ulp_project_default)
|
||||
ulp_project_init()
|
||||
|
||||
# A single executable is built, so DEFERRED optional-requires resolution is
|
||||
# safe and keeps the linked component set minimal.
|
||||
idf_build_set_property(IDF_COMPONENT_OPTIONAL_REQUIRES_MODE DEFERRED)
|
||||
|
||||
__ulp_project_default()
|
||||
endmacro()
|
||||
Reference in New Issue
Block a user