# **********************************************************************
# *                   (c) SEGGER Microcontroller GmbH                  *
# *                        The Embedded Experts                        *
# *                           www.segger.com                           *
# **********************************************************************
#
# -------------------------- END-OF-HEADER -----------------------------
#
# Purpose : CMake configuration for SEGGER sample application.
# Literature: https://cmake.org/cmake/help/latest/
#
# Notes:
#   (1) This sample project is designed to use a multi-config generator such as
#       MSVC MSBuild or "Ninja Multi-Config". Make sure to select such with
#       cmake "-G" switch accordingly. Using a single-config generator _may_ work, however
#       testing may fail at some places.
#
cmake_minimum_required(VERSION 3.24)

# At least V3.24 for link group feature: https://cmake.org/cmake/help/latest/variable/CMAKE_LINK_GROUP_USING_FEATURE.html
#    - WHen using ARM GCC/Gnu linker, we need this feature, since GNU linker does single-pass linking. SEGGER linker by default does multi-pass linking.

# --------------------------------------------------------------------------------------------------
# For SEGGER toolchain:
#   - CMAKE_SYSTEM_PROCESSOR must have been set (either in this file or passed via -D switch 
#     to cmake) _before_ the first call to project(...).
#   - These settings shall become effective by passing the "segger-toolchain.cmake" file to 
#     the --toolchain cmake commandline-option
#     (or alternatively using the -DCMAKE_TOOLCHAIN_FILE switch).
#   - Add the cmake_modules folder in order to let cmake pick up some 
#     platform/cpu/architecture-specific settings. Cmake does that in 
#     cmake-XXXX\Modules\CMake*Information.cmake with lines like these:
#        include(Platform/${CMAKE_EFFECTIVE_SYSTEM_NAME}-${CMAKE_C_COMPILER_ID}-C-${CMAKE_SYSTEM_PROCESSOR} OPTIONAL RESULT_VARIABLE _INCLUDED_FILE)
#     NOTE:
#        1. It is the responsibility of the project maintainer to provide these files and extend CMAKE_MODULE_PATH accordingly!!!
#        2. The files under ./cmake_modules in this sample project are provided for reference and demonstration only and do not cover all possible use-cases.
# --------------------------------------------------------------------------------------------------
if(NOT "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules" IN_LIST CMAKE_MODULE_PATH)
  list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules")
  message(STATUS "CMAKE_MODULE_PATH extended by ${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules")
endif()
# --------------------------------------------------------------------------------------------------

# The option "PROJECT_ENABLE_IPO" only makes sense for flat-list projects, since the SEGGER toolchain only support IPO/LTO for flat-list builds.
include(CMakeDependentOption)
option(PROJECT_FLAT_SOURCE_LIST "Enable Flat-list project setup" FALSE)
cmake_dependent_option(PROJECT_ENABLE_IPO "Enable IPO/LTO" OFF PROJECT_FLAT_SOURCE_LIST OFF)

set(CMAKE_COLOR_DIAGNOSTICS ON)

project(DemoProject LANGUAGES C CXX ASM)

#= START: workaround to force -Os for ARMGCC Release build for a faor comparison =========================================
#         Note: place this block _after_ the project(...) call or the enable_language(...) call.
if(DEFINED USE_ARM_GCC_TOOLCHAIN AND USE_ARM_GCC_TOOLCHAIN EQUAL 1)
  string(APPEND _msg "NOTE: For ARMGCC MinSizeRel builds, we modify the original compiler flags to enforce smaller generated code (-Oz). This is to ensure a fair size comparison with the SEGGER toolchain builds.")
  string(APPEND _msg "\nOriginal value: CMAKE_C_FLAGS_MINSIZEREL          = ${CMAKE_C_FLAGS_MINSIZEREL}")
  string(APPEND _msg "\nOriginal value: CMAKE_CXX_FLAGS_MINSIZEREL        = ${CMAKE_CXX_FLAGS_MINSIZEREL}")
  string(APPEND _msg "\nOriginal value: CACHE{CMAKE_C_FLAGS_MINSIZEREL}   = $CACHE{CMAKE_C_FLAGS_MINSIZEREL}")
  string(APPEND _msg "\nOriginal value: CACHE{CMAKE_CXX_FLAGS_MINSIZEREL} = $CACHE{CMAKE_CXX_FLAGS_MINSIZEREL}")

  string(REPLACE "-Os" "" CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL}")
  string(REPLACE "-Os" "" CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL}")
  string(APPEND CMAKE_C_FLAGS_MINSIZEREL " -Oz")
  string(APPEND CMAKE_CXX_FLAGS_MINSIZEREL " -Oz")

  set(CMAKE_C_FLAGS_MINSIZEREL "${CMAKE_C_FLAGS_MINSIZEREL}" CACHE STRING "" FORCE)
  set(CMAKE_CXX_FLAGS_MINSIZEREL "${CMAKE_CXX_FLAGS_MINSIZEREL}" CACHE STRING "" FORCE)

  string(APPEND _msg "\n\nModified value: CMAKE_C_FLAGS_MINSIZEREL        = ${CMAKE_C_FLAGS_MINSIZEREL}")
  string(APPEND _msg "\nModified value: CMAKE_CXX_FLAGS_MINSIZEREL        = ${CMAKE_CXX_FLAGS_MINSIZEREL}")
  string(APPEND _msg "\nModified value: CACHE{CMAKE_C_FLAGS_MINSIZEREL}   = $CACHE{CMAKE_C_FLAGS_MINSIZEREL}")
  string(APPEND _msg "\nModified value: CACHE{CMAKE_CXX_FLAGS_MINSIZEREL} = $CACHE{CMAKE_CXX_FLAGS_MINSIZEREL}")
#-----------------------------------------------------------------------------------------------------------------------
  string(APPEND _msg "\n\nNOTE: For ARMGCC RELEASE builds, we modify the original compiler flags to enforce smaller generated code (-Os). This is to ensure a fair size comparison with the SEGGER toolchain builds.")
  string(APPEND _msg "\nOriginal value: CMAKE_C_FLAGS_RELEASE          = ${CMAKE_C_FLAGS_RELEASE}")
  string(APPEND _msg "\nOriginal value: CMAKE_CXX_FLAGS_RELEASE        = ${CMAKE_CXX_FLAGS_RELEASE}")
  string(APPEND _msg "\nOriginal value: CACHE{CMAKE_C_FLAGS_RELEASE}   = $CACHE{CMAKE_C_FLAGS_RELEASE}")
  string(APPEND _msg "\nOriginal value: CACHE{CMAKE_CXX_FLAGS_RELEASE} = $CACHE{CMAKE_CXX_FLAGS_RELEASE}")

  string(REPLACE "-O3" "" CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}")
  string(REPLACE "-O3" "" CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}")
  string(APPEND CMAKE_C_FLAGS_RELEASE " -Os")
  string(APPEND CMAKE_CXX_FLAGS_RELEASE " -Os")

  set(CMAKE_C_FLAGS_RELEASE "${CMAKE_C_FLAGS_RELEASE}" CACHE STRING "" FORCE)
  set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE}" CACHE STRING "" FORCE)

  string(APPEND _msg "\n\nModified value: CMAKE_C_FLAGS_RELEASE        = ${CMAKE_C_FLAGS_RELEASE}")
  string(APPEND _msg "\nModified value: CMAKE_CXX_FLAGS_RELEASE        = ${CMAKE_CXX_FLAGS_RELEASE}")
  string(APPEND _msg "\nModified value: CACHE{CMAKE_C_FLAGS_RELEASE}   = $CACHE{CMAKE_C_FLAGS_RELEASE}")
  string(APPEND _msg "\nModified value: CACHE{CMAKE_CXX_FLAGS_RELEASE} = $CACHE{CMAKE_CXX_FLAGS_RELEASE}")
#-----------------------------------------------------------------------------------------------------------------------
endif()
#= END: workaround to force -Os for ARMGCC Release build for a faor comparison =========================================

if(DEFINED USE_ARM_GCC_TOOLCHAIN AND USE_ARM_GCC_TOOLCHAIN EQUAL 1)
  # Silence warnings related to old-style coding...:
  set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -Wno-expansion-to-defined" CACHE STRING "" FORCE)
endif()

list(JOIN CMAKE_CONFIGURATION_TYPES ", "  _cfg_list)
string(APPEND _msg "\n================================================================================================================\n")
if(DEFINED SEGGER_CMAKE_TOOLCHAIN_VERSION)
string(APPEND _msg "SEGGER_CMAKE_TOOLCHAIN_VERSION = ${SEGGER_CMAKE_TOOLCHAIN_VERSION}\n")
string(APPEND _msg "SEGGER_TOOLCHAIN_PKG_ROOT = ${SEGGER_TOOLCHAIN_PKG_ROOT}\n")
string(APPEND _msg "SEGGER_CMAKE_HAS_BUILTIN_COMPILER_ID = $CACHE{SEGGER_CMAKE_HAS_BUILTIN_COMPILER_ID}\n")
endif()
string(APPEND _msg "CMAKE_SYSTEM_NAME = " "${CMAKE_SYSTEM_NAME}\n")
string(APPEND _msg "CMAKE_SYSTEM_PROCESSOR = " "${CMAKE_SYSTEM_PROCESSOR}\n")
string(APPEND _msg "Available build configuration types = " "${_cfg_list}")
string(APPEND _msg "\n================================================================================================================\n")
message(STATUS ${_msg})
unset(_cfg_list)
unset(_msg)

#
# Define build target
#
set(DEMOAPP "CORTEX_M7_SAME70-FreeRTOS-Demo")
add_executable(${DEMOAPP})

#
# Application Sources
#
set(APP_SRC
  "../../Source/event_groups.c"
  "../../Source/list.c"
  "../../Source/portable/GCC/ARM_CM7/r0p1/port.c"
  "../../Source/portable/MemMang/heap_4.c"
  "../../Source/queue.c"
  "../../Source/tasks.c"
  "../../Source/timers.c"
  "../Common/Minimal/BlockQ.c"
  "../Common/Minimal/blocktim.c"
  "../Common/Minimal/countsem.c"
  "../Common/Minimal/death.c"
  "../Common/Minimal/dynamic.c"
  "../Common/Minimal/EventGroupsDemo.c"
  "../Common/Minimal/flop.c"
  "../Common/Minimal/GenQTest.c"
  "../Common/Minimal/IntQueue.c"
  "../Common/Minimal/IntSemTest.c"
  "../Common/Minimal/QueueOverwrite.c"
  "../Common/Minimal/recmutex.c"
  "../Common/Minimal/semtest.c"
  "../Common/Minimal/TaskNotify.c"
  "../Common/Minimal/TimerDemo.c"
  "src/ASF/common/services/clock/same70/sysclk.c"
  "src/ASF/common/services/serial/usart_serial.c"
  "src/ASF/common/utils/interrupt/interrupt_sam_nvic.c"
  "src/ASF/common/utils/stdio/read.c"
  "src/ASF/common/utils/stdio/write.c"
  "src/ASF/sam/boards/same70_xplained/init.c"
  "src/ASF/sam/drivers/matrix/matrix.c"
  "src/ASF/sam/drivers/mpu/mpu.c"
  "src/ASF/sam/drivers/pio/pio.c"
  "src/ASF/sam/drivers/pio/pio_handler.c"
  "src/ASF/sam/drivers/pmc/pmc.c"
  "src/ASF/sam/drivers/pmc/sleep.c"
  "src/ASF/sam/drivers/tc/tc.c"
  "src/ASF/sam/drivers/uart/uart.c"
  "src/ASF/sam/drivers/usart/usart.c"
  "src/ASF/sam/utils/cmsis/same70/source/templates/gcc/startup_same70.c"
  "src/ASF/sam/utils/cmsis/same70/source/templates/system_same70.c"
  "src/ASF/sam/utils/syscalls/gcc/syscalls.c"
  "src/Blinky_Demo/main_blinky.c"
  "src/FreeRTOSConfig.h"
  "src/Full_Demo/IntQueueTimer.c"
  "src/Full_Demo/main_full.c"
  "src/Full_Demo/RegTest_GCC.c"
  "src/main.c"
)

#
# Generic Project Settings
#
set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)

if(DEFINED SEGGER_USE_GCC AND SEGGER_USE_GCC GREATER 0)
  # We need at least CXX_STANDARD 11 if we want to use GCC-mode because seggers libc++ does not support using GCC with C++03. 
  set(_min_cxx_standard 11)
else()
  set(_min_cxx_standard 98)
endif()

set_target_properties(${DEMOAPP} PROPERTIES
  C_STANDARD 99
  C_EXTENSIONS OFF
  C_STANDARD_REQUIRED ON
  CXX_STANDARD ${_min_cxx_standard}
  CXX_EXTENSIONS OFF
  CXX_STANDARD_REQUIRED ON
)
unset(_min_cxx_standard)

if(PROJECT_ENABLE_IPO )
  if(DEFINED USE_ARM_GCC_TOOLCHAIN AND USE_ARM_GCC_TOOLCHAIN EQUAL 1)
    include(CheckIPOSupported)
    check_ipo_supported(RESULT result OUTPUT output )
  else()
    # SEGGER supports IPO, but check_ipo_supported fails because it uses try_compile( x PROJECT ...) which will create a library (foo.a) and an object file (main.o) and try to link them.
    # However: the segger-linker does not support using libraries for LTO linking and therefore this approach will fail.
    # So we force the result to TRUE explicitly for SEGGER.
    set(result TRUE)
  endif()

  if(result)
    if(NOT DEFINED USE_ARM_GCC_TOOLCHAIN)
      # The "nice" way to do this is to simply set the target property INTERPROCEDURAL_OPTIMIZATION to TRUE.
      # However, the symbol "Reset_Handler" gets optimized away by the LTO compiler stage when using the SEGGER compiler. Thus we need to tell the LTO compiler stage
      # to keep that symbol. Unfortunately if the SEGGER compiler driver gets "-flto -KReset_Handler" in that order, then the "-KReset_Handler" option seems to be ignored.
      # If we pass "-KReset_Handler -flto" in this order, then things are fine.
      # HOWEVER!: CMake seems to always place the switches in this (relative) order on the command-line:
      #   - CMAKE_<LANG>_FLAGS
      #   - target property INTERPROCEDURAL_OPTIMIZATION
      #   - defined by target_compile_options(...)
      #   - set_source_files_properties(...)
      #
      # So we cannot nicely add -KReset_Handler on either a per-target or (even better) a per-file basis!
      # The only remaining method is to append/prepend "-KReset_Handler" to the CMAKE_<LANG>_FLAGS strings.

      # Same thing applies to the exception table symbol (which is in the .vectors section and should not be optimized away)

      string(APPEND CMAKE_C_FLAGS " -KReset_Handler -Kexception_table")
      string(APPEND CMAKE_CXX_FLAGS " -KReset_Handler -Kexception_table")
      string(APPEND CMAKE_ASM_FLAGS " -KReset_Handler -Kexception_table")
    endif()
    # Would be nicer if it worked: set_property(SOURCE src/ASF/sam/utils/cmsis/same70/source/templates/gcc/startup_same70.c PROPERTY COMPILE_OPTIONS "-KReset_Handler")
    set_property(TARGET ${DEMOAPP} APPEND PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)
  else()
    message(FATAL_ERROR "IPO is not supported: ${output}")
  endif()
endif()

#
# Set global preprocessor definitions here.
# NOTE: this must be done before calls to add_subdirectory(...) in order to make these
#       definition effective for the added sub-directories.
#
add_compile_definitions(
  #-START: imported from RTOSDemo.cproj
  __SAME70Q21__
  BOARD=SAME70_XPLAINED
  scanf=iscanf
  ARM_MATH_CM7=true
  printf=iprintf
  # Need to increase the stack size for "Notified" Task!
  notifyNOTIFIED_TASK_STACK_SIZE=150
  #-END: imported from RTOSDemo.cproj
)

if(DEFINED USE_ARM_GCC_TOOLCHAIN AND USE_ARM_GCC_TOOLCHAIN EQUAL 1)
else()
  target_compile_definitions(${DEMOAPP} PRIVATE
    __SEGGER_LINKER
  )
  target_sources(${DEMOAPP} PRIVATE
    "Toolchains/Segger/SEGGER_THUMB_Startup.s"
  )
endif()

# Add the additional device-specific files to the source-list for the application.
target_sources(${DEMOAPP} PRIVATE
  ${APP_SRC}
)


if(PROJECT_FLAT_SOURCE_LIST)
  target_include_directories(${DEMOAPP} PRIVATE
    src/ASF/common/utils
    src/ASF/sam/boards/same70_xplained
    src/ASF/sam/utils/fpu
    src/ASF/common/services/serial/sam_uart
    src/ASF/sam/drivers/matrix
    src/ASF/sam/drivers/pmc
    src/ASF/common/services/gpio
    src/ASF/sam/boards
    src/ASF/sam/drivers/pio
    src/ASF/common/boards
    src/ASF/sam/utils/header_files
    src/ASF/sam/drivers/tc
    src/ASF/common/services/ioport
    src/ASF/sam/drivers/mpu
    src/ASF/thirdparty/CMSIS/Include
    src/config
    src/ASF/thirdparty/CMSIS/Lib/GCC
    src
    src/ASF/common/services/clock
    src/ASF/sam/utils/cmsis/same70/source/templates
    src/ASF/common/utils/stdio/stdio_serial
    src/ASF/sam/utils
    src/ASF/sam/utils/preprocessor
    src/ASF/common/services/serial
    src/ASF/sam/drivers/usart
    src/ASF/sam/drivers/uart
    src/ASF/sam/utils/cmsis/same70/include
    sam/applications/getting-started/same70q21_same70_xplained
    src/ASF/common/utils
    src/ASF/sam/boards/same70_xplained
    src/ASF/sam/utils/fpu
    src/ASF/common/services/serial/sam_uart
    src/ASF/sam/drivers/matrix
    src/ASF/sam/drivers/pmc
    src/ASF/common/services/gpio
    src/ASF/sam/boards
    src/ASF/sam/drivers/pio
    src/ASF/common/boards
    src/ASF/sam/utils/header_files
    src/ASF/sam/drivers/tc
    src/ASF/common/services/ioport
    src/ASF/sam/drivers/mpu
    src/ASF/thirdparty/CMSIS/Include
    src/config
    src/ASF/thirdparty/CMSIS/Lib/GCC
    src
    src/ASF/common/services/clock
    src/ASF/sam/utils/cmsis/same70/source/templates
    src/ASF/common/utils/stdio/stdio_serial
    src/ASF/sam/utils
    src/ASF/sam/utils/preprocessor
    src/ASF/common/services/serial
    src/ASF/sam/drivers/usart
    src/ASF/sam/drivers/uart
    src/ASF/sam/utils/cmsis/same70/include
    ../../Source/include
    ../../Source/portable/GCC/ARM_CM7/r0p1
    ../Common/include
    src/Full_Demo
)
else()
  if(DEFINED USE_ARM_GCC_TOOLCHAIN AND USE_ARM_GCC_TOOLCHAIN EQUAL 1)
    # Force multi-pass symbol-resolution when using GNU ld (from ARM GCC). SEGGER linker does that by default.
    set(CMAKE_C_LINK_GROUP_USING_cross_refs_SUPPORTED TRUE)
    set(CMAKE_C_LINK_GROUP_USING_cross_refs
      "LINKER:--start-group"
      "LINKER:--end-group"
    )
    LIST(JOIN liblist_DemoApp "," libcommalist_DemoApp)
    set(liblist_DemoApp "$<LINK_GROUP:cross_refs,${libcommalist_DemoApp}>")
  endif()

  target_link_libraries(${DEMOAPP} PRIVATE
    ${liblist_DemoApp}
  )
endif()

# ==========================================================================================================================================
#
# CPU specific code and linker scripts
#
if(CMAKE_SYSTEM_NAME MATCHES "Generic|Generic-ELF")
  # Cross-Compiling for embedded target detected.
  if(NOT CMAKE_SYSTEM_PROCESSOR MATCHES "arm-.*" AND NOT CMAKE_SYSTEM_PROCESSOR STREQUAL "ARM")
    message(FATAL_ERROR "CMAKE_SYSTEM_PROCESSOR (${CMAKE_SYSTEM_PROCESSOR}) undefined or unsupported value. Please specify it on commandline using -DCMAKE_SYSTEM_PROCESSOR=<value> or in your CMakeLists.txt !")
  endif()

  # Add .elf suffix to output file. Required for GNU/gcc mode, since by default 
  # CMake sets CMAKE_EXECUTABLE_SUFFIX to "" for GNU.
  set(CMAKE_EXECUTABLE_SUFFIX ".elf")

  set(_app_entry "Reset_Handler")

  if(DEFINED USE_ARM_GCC_TOOLCHAIN AND USE_ARM_GCC_TOOLCHAIN EQUAL 1)
    # Use ARM GNU toolchain.

    # Patched linker-script:
    # For GCC versions greater or equal to 13.1.0:
    #  Add the READONLY attribute to .init_array and .fini_array sections, otherwise we may get warnings like "warning: my_elf_file.elf has a LOAD segment with RWX permissions"
    #  See also https://stackoverflow.com/questions/73429929/gnu-linker-elf-has-a-load-segment-with-rwx-permissions-embedded-arm-project
    #
    #  We will let CMake patch-in the "READONLY" attribute for us.
    if(CMAKE_C_COMPILER_VERSION VERSION_GREATER 10.3.1)
      set(LINKER_ARRAY_ATTRIBS "READONLY")
    else()
      set(LINKER_ARRAY_ATTRIBS "")
    endif()

    cmake_path(SET LINKER_SCRIPT ${CMAKE_CURRENT_SOURCE_DIR}/src/ASF/sam/utils/linker_scripts/same70/same70q21/gcc/flash.ld)
    target_link_options(${DEMOAPP} PRIVATE
      # -v
      -Wl,-T ${LINKER_SCRIPT}
      -Wl,-entry,${_app_entry}
      -Wl,--no-dynamic-linker
      -Wl,--warn-common
      -Wl,--fatal-warnings
      -Wl,--gc-sections
      -Wl,--print-memory-usage
      -Wl,--print-map-discarded
      -Wl,--cref
      -Wl,--print-map
      -Wl,-Map=$<PATH:REPLACE_EXTENSION,$<TARGET_FILE:$<TARGET_PROPERTY:NAME>>,.map>
      -Wl,--unresolved-symbols=report-all
      -Wl,-z,defs
      -Wl,-static
    )
  else()
    # Assume we are using SEGGER toolchain
    cmake_path(SET LINKER_SCRIPT "${CMAKE_CURRENT_SOURCE_DIR}/Toolchains/Segger/segger_flash.icf")
    target_link_options(${DEMOAPP} PRIVATE
      -T ${LINKER_SCRIPT}
      -Wl,--verbose
      # Set memory regions of device
      #   - moved to linker script

      # Application-specific configuration
      #   - moved defsmys to linker script

      # I/O Configuration
      #  - none required by this particular application

      # Entry Point
      -e${_app_entry}

      # Optional settings
      # -Wl,--auto-es-block-symbols
      -Wl,--remarks-are-errors
      -Wl,--warnings-are-errors
      -Wl,--no-force-output
      -Wl,--fatal-warnings
      -Wl,--gc-sections
      -Wl,--full-section-headers
      -Wl,--merge-sections
      -Wl,--merge-strings
      -Wl,--map-full
      -Wl,--map-unused-inputs
      -Wl,--no-tail-merge
      -Wl,--list-all-undefineds
      #------------------------------------------------
    )
  endif()

  set_target_properties(${DEMOAPP} PROPERTIES
    LINK_DEPENDS "${LINKER_SCRIPT};${CMAKE_CURRENT_LIST_FILE}"
  )

endif()
# **************************** end of file *****************************
