include(stm32-bootloader) include(stm32f4) include(stm32f7) include(stm32h7) include(CMakeParseArguments) option(DEBUG_HARDFAULTS "Enable debugging of hard faults via custom handler") option(SEMIHOSTING "Enable semihosting") message("-- DEBUG_HARDFAULTS: ${DEBUG_HARDFAULTS}, SEMIHOSTING: ${SEMIHOSTING}") set(CMSIS_DIR "${MAIN_LIB_DIR}/main/CMSIS") set(CMSIS_INCLUDE_DIR "${CMSIS_DIR}/Core/Include") set(CMSIS_DSP_DIR "${MAIN_LIB_DIR}/main/CMSIS/DSP") set(CMSIS_DSP_INCLUDE_DIR "${CMSIS_DSP_DIR}/Include") set(CMSIS_DSP_SRC BasicMathFunctions/arm_scale_f32.c BasicMathFunctions/arm_sub_f32.c BasicMathFunctions/arm_mult_f32.c BasicMathFunctions/arm_offset_f32.c TransformFunctions/arm_rfft_fast_f32.c TransformFunctions/arm_cfft_f32.c TransformFunctions/arm_rfft_fast_init_f32.c TransformFunctions/arm_cfft_radix8_f32.c TransformFunctions/arm_bitreversal2.S CommonTables/arm_common_tables.c ComplexMathFunctions/arm_cmplx_mag_f32.c StatisticsFunctions/arm_max_f32.c StatisticsFunctions/arm_rms_f32.c StatisticsFunctions/arm_std_f32.c StatisticsFunctions/arm_mean_f32.c ) list(TRANSFORM CMSIS_DSP_SRC PREPEND "${CMSIS_DSP_DIR}/Source/") set(STM32_STARTUP_DIR "${MAIN_SRC_DIR}/startup") main_sources(STM32_VCP_SRC drivers/serial_usb_vcp.c drivers/usb_io.c ) main_sources(STM32_SDCARD_SRC drivers/sdcard/sdcard.c drivers/sdcard/sdcard_spi.c drivers/sdcard/sdcard_sdio.c drivers/sdcard/sdcard_standard.c ) # XXX: This code is not STM32 specific main_sources(STM32_ASYNCFATFS_SRC io/asyncfatfs/asyncfatfs.c io/asyncfatfs/fat_standard.c ) main_sources(STM32_MSC_SRC msc/usbd_storage.c ) main_sources(STM32_MSC_FLASH_SRC msc/usbd_storage_emfat.c msc/emfat.c msc/emfat_file.c ) main_sources(STM32_MSC_SDCARD_SRC msc/usbd_storage_sd_spi.c ) set(STM32_INCLUDE_DIRS "${CMSIS_INCLUDE_DIR}" "${CMSIS_DSP_INCLUDE_DIR}" "${MAIN_SRC_DIR}/target" ) set(STM32_DEFINITIONS ) set(STM32_DEFAULT_HSE_MHZ 8) set(STM32_LINKER_DIR "${MAIN_SRC_DIR}/target/link") set(STM32_COMPILE_OPTIONS -ffunction-sections -fdata-sections -fno-common ) set(STM32_LINK_LIBRARIES -lm -lc ) if(SEMIHOSTING) list(APPEND STM32_LINK_LIBRARIES --specs=rdimon.specs -lrdimon) list(APPEND STM32_DEFINITIONS SEMIHOSTING) else() list(APPEND STM32_LINK_LIBRARIES -lnosys) endif() set(STM32_LINK_OPTIONS -nostartfiles --specs=nano.specs -static -Wl,-gc-sections -Wl,-L${STM32_LINKER_DIR} -Wl,--cref -Wl,--no-wchar-size-warning -Wl,--print-memory-usage ) macro(get_stm32_target_features output_var dir target_name) execute_process(COMMAND "${CMAKE_C_COMPILER}" -E -dD -D${ARGV2} "${ARGV1}/target.h" ERROR_VARIABLE _errors RESULT_VARIABLE _result OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE _contents) if(NOT _result EQUAL 0) message(FATAL_ERROR "error extracting features for stm32 target ${ARGV2}: ${_errors}") endif() string(REGEX MATCH "#define[\t ]+USE_VCP" HAS_VCP ${_contents}) if(HAS_VCP) list(APPEND ${ARGV0} VCP) endif() string(REGEX MATCH "define[\t ]+USE_FLASHFS" HAS_FLASHFS ${_contents}) if(HAS_FLASHFS) list(APPEND ${ARGV0} FLASHFS) endif() string(REGEX MATCH "define[\t ]+USE_SDCARD" HAS_SDCARD ${_contents}) if (HAS_SDCARD) list(APPEND ${ARGV0} SDCARD) string(REGEX MATCH "define[\t ]+USE_SDCARD_SDIO" HAS_SDIO ${_contents}) if (HAS_SDIO) list(APPEND ${ARGV0} SDIO) endif() endif() if(HAS_FLASHFS OR HAS_SDCARD) list(APPEND ${ARGV0} MSC) endif() endmacro() function(get_stm32_flash_size out size) # 4: 16, 6: 32, 8: 64, B: 128, C: 256, D: 384, E: 512, F: 768, G: 1024, H: 1536, I: 2048 KiB string(TOUPPER ${size} s) if(${s} STREQUAL "4") set(${out} 16 PARENT_SCOPE) return() endif() if(${s} STREQUAL "6") set(${out} 32 PARENT_SCOPE) return() endif() if(${s} STREQUAL "8") set(${out} 64 PARENT_SCOPE) return() endif() if(${s} STREQUAL "8") set(${out} 64 PARENT_SCOPE) return() endif() if(${s} STREQUAL "B") set(${out} 128 PARENT_SCOPE) return() endif() if(${s} STREQUAL "C") set(${out} 256 PARENT_SCOPE) return() endif() if(${s} STREQUAL "D") set(${out} 384 PARENT_SCOPE) return() endif() if(${s} STREQUAL "E") set(${out} 512 PARENT_SCOPE) return() endif() if(${s} STREQUAL "F") set(${out} 768 PARENT_SCOPE) return() endif() if(${s} STREQUAL "G") set(${out} 1024 PARENT_SCOPE) return() endif() if(${s} STREQUAL "H") set(${out} 1536 PARENT_SCOPE) return() endif() if(${s} STREQUAL "I") set(${out} 2048 PARENT_SCOPE) return() endif() endfunction() function(add_hex_target name exe hex) add_custom_target(${name} ALL cmake -E env PATH="$ENV{PATH}" # TODO: Overriding the start address with --set-start 0x08000000 # seems to be required due to some incorrect assumptions about .hex # files in the configurator. Verify wether that's the case and fix # the bug in configurator or delete this comment. ${CMAKE_OBJCOPY} -Oihex --set-start 0x08000000 $ ${hex} BYPRODUCTS ${hex} ) endfunction() function(add_bin_target name exe bin) add_custom_target(${name} cmake -E env PATH="$ENV{PATH}" ${CMAKE_OBJCOPY} -Obinary $ ${bin} BYPRODUCTS ${bin} ) endfunction() function(generate_map_file target) if(CMAKE_VERSION VERSION_LESS 3.15) set(map "$.map") else() set(map "$/$.map") endif() target_link_options(${target} PRIVATE "-Wl,-Map,${map}") endfunction() function(set_linker_script target script) set(script_path ${STM32_LINKER_DIR}/${args_LINKER_SCRIPT}.ld) if(NOT EXISTS ${script_path}) message(FATAL_ERROR "linker script ${script_path} doesn't exist") endif() set_target_properties(${target} PROPERTIES LINK_DEPENDS ${script_path}) target_link_options(${elf_target} PRIVATE -T${script_path}) endfunction() function(add_stm32_executable) cmake_parse_arguments( args # Boolean arguments "" # Single value arguments "FILENAME;NAME;OPTIMIZATION;OUTPUT_BIN_FILENAME;OUTPUT_HEX_FILENAME;OUTPUT_TARGET_NAME" # Multi-value arguments "COMPILE_DEFINITIONS;COMPILE_OPTIONS;INCLUDE_DIRECTORIES;LINK_OPTIONS;LINKER_SCRIPT;SOURCES" # Start parsing after the known arguments ${ARGN} ) set(elf_target ${args_NAME}.elf) add_executable(${elf_target}) target_sources(${elf_target} PRIVATE ${args_SOURCES}) target_include_directories(${elf_target} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR} ${args_INCLUDE_DIRECTORIES} ${STM32_INCLUDE_DIRS}) target_compile_definitions(${elf_target} PRIVATE ${args_COMPILE_DEFINITIONS}) target_compile_options(${elf_target} PRIVATE ${STM32_COMPILE_OPTIONS} ${args_COMPILE_OPTIONS}) if(WARNINGS_AS_ERRORS) target_compile_options(${elf_target} PRIVATE -Werror) endif() if (IS_RELEASE_BUILD) target_compile_options(${elf_target} PRIVATE ${args_OPTIMIZATION}) target_link_options(${elf_target} PRIVATE ${args_OPTIMIZATION}) endif() target_link_libraries(${elf_target} PRIVATE ${STM32_LINK_LIBRARIES}) target_link_options(${elf_target} PRIVATE ${STM32_LINK_OPTIONS} ${args_LINK_OPTIONS}) generate_map_file(${elf_target}) set_linker_script(${elf_target} ${args_LINKER_SCRIPT}) if(args_FILENAME) set(basename ${CMAKE_BINARY_DIR}/${args_FILENAME}) set(hex_filename ${basename}.hex) add_hex_target(${args_NAME} ${elf_target} ${hex_filename}) set(bin_filename ${basename}.bin) add_bin_target(${args_NAME}.bin ${elf_target} ${bin_filename}) endif() if(args_OUTPUT_BIN_FILENAME) set(${args_OUTPUT_BIN_FILENAME} ${bin_filename} PARENT_SCOPE) endif() if(args_OUTPUT_TARGET_NAME) set(${args_OUTPUT_TARGET_NAME} ${elf_target} PARENT_SCOPE) endif() if(args_OUTPUT_HEX_FILENAME) set(${args_OUTPUT_HEX_FILENAME} ${hex_filename} PARENT_SCOPE) endif() endfunction() function(target_stm32) if(NOT arm-none-eabi STREQUAL TOOLCHAIN) return() endif() # Parse keyword arguments cmake_parse_arguments( args # Boolean arguments "DISABLE_MSC;BOOTLOADER" # Single value arguments "HSE_MHZ;LINKER_SCRIPT;NAME;OPENOCD_TARGET;OPTIMIZATION;STARTUP;SVD" # Multi-value arguments "COMPILE_DEFINITIONS;COMPILE_OPTIONS;INCLUDE_DIRECTORIES;LINK_OPTIONS;SOURCES;MSC_SOURCES;MSC_INCLUDE_DIRECTORIES;VCP_SOURCES;VCP_INCLUDE_DIRECTORIES" # Start parsing after the known arguments ${ARGN} ) set(name ${args_NAME}) if (args_HSE_MHZ) set(hse_mhz ${args_HSE_MHZ}) else() set(hse_mhz ${STM32_DEFAULT_HSE_MHZ}) endif() set(target_sources ${STM32_STARTUP_DIR}/${args_STARTUP}) list(APPEND target_sources ${args_SOURCES}) file(GLOB target_c_sources "${CMAKE_CURRENT_SOURCE_DIR}/*.c") file(GLOB target_h_sources "${CMAKE_CURRENT_SOURCE_DIR}/*.h") list(APPEND target_sources ${target_c_sources} ${target_h_sources}) set(target_include_directories ${args_INCLUDE_DIRECTORIES}) set(target_definitions ${STM32_DEFINITIONS} ${COMMON_COMPILE_DEFINITIONS}) get_stm32_target_features(features "${CMAKE_CURRENT_SOURCE_DIR}" ${name}) set_property(TARGET ${elf_target} PROPERTY FEATURES ${features}) if(VCP IN_LIST features) list(APPEND target_sources ${STM32_VCP_SRC} ${args_VCP_SOURCES}) list(APPEND target_include_directories ${args_VCP_INCLUDE_DIRECTORIES}) endif() if(SDCARD IN_LIST features) list(APPEND target_sources ${STM32_SDCARD_SRC} ${STM32_ASYNCFATFS_SRC}) endif() set(msc_sources) if(NOT args_DISABLE_MSC AND MSC IN_LIST features) list(APPEND target_include_directories ${args_MSC_INCLUDE_DIRECTORIES}) list(APPEND msc_sources ${STM32_MSC_SRC} ${args_MSC_SOURCES}) list(APPEND target_definitions USE_USB_MSC) if(FLASHFS IN_LIST features) list(APPEND msc_sources ${STM32_MSC_FLASH_SRC}) endif() if (SDCARD IN_LIST features) list(APPEND msc_sources ${STM32_MSC_SDCARD_SRC}) endif() endif() math(EXPR hse_value "${hse_mhz} * 1000000") list(APPEND target_definitions "HSE_VALUE=${hse_value}") if (MSP_UART) list(APPEND target_definitions "MSP_UART=${MSP_UART}") endif() if(args_COMPILE_DEFINITIONS) list(APPEND target_definitions ${args_COMPILE_DEFINITIONS}) endif() if(DEBUG_HARDFAULTS) list(APPEND target_definitions DEBUG_HARDFAULTS) endif() string(TOLOWER ${PROJECT_NAME} lowercase_project_name) set(binary_name ${lowercase_project_name}_${FIRMWARE_VERSION}_${name}) if(DEFINED BUILD_SUFFIX AND NOT "" STREQUAL "${BUILD_SUFFIX}") set(binary_name "${binary_name}_${BUILD_SUFFIX}") endif() # Main firmware add_stm32_executable( NAME ${name} FILENAME ${binary_name} SOURCES ${target_sources} ${msc_sources} ${CMSIS_DSP_SRC} ${COMMON_SRC} COMPILE_DEFINITIONS ${target_definitions} COMPILE_OPTIONS ${args_COMPILE_OPTIONS} INCLUDE_DIRECTORIES ${target_include_directories} LINK_OPTIONS ${args_LINK_OPTIONS} LINKER_SCRIPT ${args_LINKER_SCRIPT} OPTIMIZATION ${args_OPTIMIZATION} OUTPUT_HEX_FILENAME main_hex_filename OUTPUT_TARGET_NAME main_target_name ) set_property(TARGET ${main_target_name} PROPERTY OPENOCD_TARGET ${args_OPENOCD_TARGET}) set_property(TARGET ${main_target_name} PROPERTY OPENOCD_DEFAULT_INTERFACE stlink) set_property(TARGET ${main_target_name} PROPERTY SVD ${args_SVD}) setup_firmware_target(${main_target_name} ${name} ${ARGN}) if(args_BOOTLOADER) # Bootloader for the target set(bl_suffix _bl) add_stm32_executable( NAME ${name}${bl_suffix} FILENAME ${binary_name}${bl_suffix} SOURCES ${target_sources} ${BOOTLOADER_SOURCES} COMPILE_DEFINITIONS ${target_definitions} BOOTLOADER MSP_FIRMWARE_UPDATE COMPILE_OPTIONS ${args_COMPILE_OPTIONS} INCLUDE_DIRECTORIES ${target_include_directories} LINK_OPTIONS ${args_LINK_OPTIONS} LINKER_SCRIPT ${args_LINKER_SCRIPT}${bl_suffix} OPTIMIZATION ${args_OPTIMIZATION} OUTPUT_BIN_FILENAME bl_bin_filename OUTPUT_HEX_FILENAME bl_hex_filename OUTPUT_TARGET_NAME bl_target_name ) setup_executable(${bl_target_name} ${name}) # Main firmware, but for running with the bootloader set(for_bl_suffix _for_bl) add_stm32_executable( NAME ${name}${for_bl_suffix} FILENAME ${binary_name}${for_bl_suffix} SOURCES ${target_sources} ${msc_sources} ${CMSIS_DSP_SRC} ${COMMON_SRC} COMPILE_DEFINITIONS ${target_definitions} MSP_FIRMWARE_UPDATE COMPILE_OPTIONS ${args_COMPILE_OPTIONS} INCLUDE_DIRECTORIES ${target_include_directories} LINK_OPTIONS ${args_LINK_OPTIONS} LINKER_SCRIPT ${args_LINKER_SCRIPT}${for_bl_suffix} OPTIMIZATION ${args_OPTIMIZATION} OUTPUT_BIN_FILENAME for_bl_bin_filename OUTPUT_HEX_FILENAME for_bl_hex_filename OUTPUT_TARGET_NAME for_bl_target_name ) setup_executable(${for_bl_target_name} ${name}) # Combined with bootloader and main firmware set(with_bl_suffix _with_bl) set(combined_hex ${CMAKE_BINARY_DIR}/${binary_name}${with_bl_suffix}.hex) set(with_bl_target ${name}${with_bl_suffix}) add_custom_target(${with_bl_target} ${CMAKE_SOURCE_DIR}/src/utils/combine_tool ${bl_bin_filename} ${for_bl_bin_filename} ${combined_hex} BYPRODUCTS ${combined_hex} ) add_dependencies(${with_bl_target} ${bl_target_name} ${for_bl_target_name}) endif() # clean_ set(generator_cmd "") if (CMAKE_GENERATOR STREQUAL "Unix Makefiles") set(generator_cmd "make") elseif(CMAKE_GENERATOR STREQUAL "Ninja") set(generator_cmd "ninja") endif() if (NOT generator_cmd STREQUAL "") set(clean_target "clean_${name}") add_custom_target(${clean_target} WORKING_DIRECTORY ${CMAKE_BINARY_DIR} COMMAND ${generator_cmd} clean COMMENT "Removing intermediate files for ${name}") set_property(TARGET ${clean_target} PROPERTY EXCLUDE_FROM_ALL 1 EXCLUDE_FROM_DEFAULT_BUILD 1) endif() endfunction()