# SEGGER Toolchain with CMake

This repository contains the implementation for using the SEGGER Toolchain with CMake.

## Background
The CMake project has integrated support for multiple compilers as GCC and Clang. Such compilers that are "known" by CMake are identified as those and CMake configures itself to fit to their interface and behavior.
As of writing, the SEGGER toolchain/compiler is not natively "known" by CMake, though SEGGER may possibly contribute native suuport to future CMake upstream versions.

While the SEGGER Compiler is based on Clang, it has different CLI options etc..
CMake provides the possibility to specify a so-called toolchain file with all related settings in order to make CMake utilize other compilers/toolchains with their specific settings. Such a file needs to be passed to the CMake configure call (see https://cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html for details).\
The toolchain file provided by this repository allows the usage of cmake executable with and without built-in SEGGER ID detection.
If a cmake executable without built-in SEGGER compiler identification is used, then the provided segger-toolchain.cmake shall detect that situation and activate some workaround methods (provided as part of the segger-toolchain-cmake package in the "segger_cmake_modules" subfolder) in order to enforce compiler/assembler ID for SEGGER and correct binutils configuration.

# Using the SEGGER Toolchain with CMake Integration
The core component of this package is the `segger-toolchain.cmake` CMake Toolchain file,
which enables easy use of the SEGGER Toolchain with CMake projects and Ninja or make build systems.

This guide provides a quick start based on a sample project
and further information for existing and new projects.

## Prerequisites
* SEGGER Toolchain with CMake Integration (this package)
* [SEGGER Embedded Studio](https://www.segger.com/downloads/embedded-studio), version ```8.24``` or later
* [Ninja](https://ninja-build.org), added to the ```PATH``` environment variable
* [CMake](https://cmake.org/download), version ```3.22.0``` or later added to the ```PATH``` environment variable
* [Visual Studio Code](https://code.visualstudio.com/download)
  * CMake Tools VS Code Extension (https://marketplace.visualstudio.com/items?itemName=ms-vscode.cmake-tools)
  * (optional, for debugging) VS Code extension "cortex-debug" by Marus25 (https://marketplace.visualstudio.com/items?itemName=marus25.cortex-debug)
  * (optional, for debugging) [J-Link Software and Documentation Pack](https://www.segger.com/downloads/jlink)
  * (optional, for debugging) Arm GNU Toolchain (https://developer.arm.com/downloads/-/gnu-rm)
    - **NOTE**: Newer versions can be found here, but are currently NOT recommended due to various issues encountered with them: https://developer.arm.com/downloads/-/arm-gnu-toolchain-downloads

## Quick Start using the Sample Project
The **segger-toolchain-cmake** package includes a simple ready-to-run sample project, 
which demonstrates the integration of the SEGGER Toolchain into a CMake project.\
Download the **segger-toolchain-cmake** package and place it at a convenient location (we'll call that folder `SGR_CM_TC` in this description):
- For Windows: `%HOMEPATH%\SGR_CM_TC`
- For posix systems (Linux etc.): `$HOME/SGR_CM_TC`
- The resulting folder structure/content should look like this _(only displaying relevant components here)_:
  ```
  tree.com %HOMEPATH%\SGR_CM_TC
  Folder PATH listing
  C:\...\SGR_CM_TC
  |   Readme.md
  |   segger-toolchain.cmake
  +---Samples
  |   \---MltCfgApp
  |       |   CMakeLists.txt
  |       |   CMakePresets.json
  |       +---.vscode
  |       +---bsp
  |       +---cmake_modules
  |       \---source
  \---segger_cmake_modules
      \---Compiler
              SEGGERClang-ASM.cmake
              SEGGERClang-C.cmake
              SEGGERClang-CXX.cmake
              SEGGERClang-FindBinUtils.cmake
              SEGGERClang.cmake
  ```

### Using VS Code
* Open `SGR_CM_TC/Samples/MltCfgApp` in VS Code
* Select the CMake Extension sidebar
* In *Project Status -> Configure* select the configuration to use (*segger_arm_cortex_m4*)
* Click on the Configure icon (or hit *Ctrl-Shift-P* and enter *CMake: Configure*)
* In *Project Status -> Build* select the build configuration to use (*debug_build_segger_arm_cortex_m4*)
* Click on the Build icon (or hit *F7* or hit *Ctrl-Shift-P* and enter *CMake: Build*)

The test application has now been built.
It can now run through some tests using CTest or be loaded onto a suitable target hardware (we will use the SEGGER emPower Board) for debugging.

#### Testing with CTest
* Select the CTest Extension sidebar
* Click on Run Tests icon (or hit *Ctrl-Shift-P* and enter *CMake: Run Tests*)

#### Debugging on target hardware
* Open `.vscode/launch.json` in the editor
* Set the *"serverpath"* to the J-Link GDB Server CL of your J-Link installation (`C:/Program Files/SEGGER/JLink/JLinkGDBServerCL.exe`)
* Set the *"gdbPath"* to the GDB Server from your Arm GNU Toolchain installation (`C:/tools/gcc-arm-none-eabi-10.3-2021.10/bin/arm-none-eabi-gdb.exe`)
* Select the Run and Debug sidebar
* Select *Debug with J-Link* as Debug Configuration
* Connect J-Link and target hardware
* Click Start Debugging icon (or hit *F5* or hit *Ctrl-Shift-P* and enter *Debug: Start Debugging*)

The test application gets loaded via J-Link to the target hardware and can now be debugged.\
**NOTE:** Refer also to the section [Details on using CMake in VSCode](#details-on-using-cmake-in-vscode) for more information

### Using the command line
* In a command-line shell, change directory to the `SGR_CM_TC/Samples/MltCfgApp` folder:
  - For Windows:
    ```cmd
    cd %HOMEPATH%\SGR_CM_TC\Samples\MltCfgApp
    ```
  - For posix systems:
    ```bash
    cd $HOME/SGR_CM_TC/Samples/MltCfgApp
    ```
* Let CMake **configure** the project for the build-folder named ```"cbuild"```:\
  _(This will create multiple build configurations e.g. "Debug", "Release" etc. as subfolders of the ```"cbuild"```-folder)._
  - For Windows:
    ```cmd
    cmake -S . -B cbuild -G"Ninja Multi-Config" --toolchain %HOMEPATH%\SGR_CM_TC\segger-toolchain.cmake -DCMAKE_SYSTEM_PROCESSOR=arm-cortex-m4
    ```
  - For posix systems:
    ```bash
    cmake -S . -B cbuild -G"Ninja Multi-Config" --toolchain $HOME/SGR_CM_TC/segger-toolchain.cmake -DCMAKE_SYSTEM_PROCESSOR=arm-cortex-m4
    ```
  - <details><summary>Example output:</summary>

    ```cmd
    C:\Users\mighty-mouse\SGR_CM_TC\Samples\MltCfgApp>cmake -S . -B cbuild -G"Ninja Multi-Config" --toolchain %HOMEPATH%\SGR_CM_TC\segger-toolchain.cmake -DCMAKE_SYSTEM_PROCESSOR=arm-cortex-m4
    -- The C compiler identification is SEGGERClang
    -- Found binutils: arm-none-eabi
    -- The CXX compiler identification is SEGGERClang
    -- Found binutils: arm-none-eabi
    -- The ASM compiler identification is SEGGERClang
    -- Found binutils: arm-none-eabi
    -- Found assembler: C:/Program Files/SEGGER/SEGGER Embedded Studio 8.24/bin/cc-segger.exe
    -- Detecting C compiler ABI info
    -- Detecting C compiler ABI info - done
    -- Check for working C compiler: C:/Program Files/SEGGER/SEGGER Embedded Studio 8.24/bin/cc-segger.exe - skipped
    -- Detecting CXX compiler ABI info
    -- Detecting CXX compiler ABI info - done
    -- Check for working CXX compiler: C:/Program Files/SEGGER/SEGGER Embedded Studio 8.24/bin/cc++-segger.exe - skipped
    --
    ================================================================================================================
    SEGGER_CMAKE_TOOLCHAIN_VERSION = 1.0.3
    SEGGER_TOOLCHAIN_PKG_ROOT = C:/Program Files/SEGGER/SEGGER Embedded Studio 8.24
    Available build configuration types = Debug, Release, RelWithDebInfo
    ================================================================================================================

    -- Configuring done
    -- Generating done
    -- Build files have been written to: C:/Users/mighty-mouse/SGR_CM_TC/Samples/MltCfgApp/cbuild
    ```
  </details>
* Let CMake **build** the project for the desired build-configuration ("Debug"):
  ```bash
  cmake --build cbuild --config Debug
  # The output file will be written to the "cbuild/Debug" directory.
  ```
  <details><summary>Example output:</summary>

  ```cmd
  C:\Users\mighty-mouse\SGR_CM_TC\Samples\MltCfgApp>cmake --build cbuild --config Debug
  [7/7] Linking CXX executable Debug\SimpleApp.elf


  C:\Users\mighty-mouse\SGR_CM_TC\Samples\MltCfgApp>dir cbuild\Debug
  Volume in drive C has no label.

  Directory of C:\Users\mighty-mouse\SGR_CM_TC\Samples\MltCfgApp\cbuild\Debug

  23/06/2025  15:46    <DIR>          .
  23/06/2025  15:46    <DIR>          ..
  23/06/2025  15:46           377.341 SimpleApp.elf
  23/06/2025  15:46         1.090.080 SimpleApp.map
                2 File(s)      1.467.421 bytes
                2 Dir(s)  862.768.513.024 bytes free
  ```
  </details>

## Adding the SEGGER Toolchain to an existing project

The SEGGER Toolchain can easily be added to existing projects, which already use CMake, Ninja or make, and a different Toolchain such as gcc.

The basic steps are:

1. Add the **segger-toolchain-cmake** package to the project.
1. Add SEGGER Toolchain-specific files to the project.
1. Adapt the project CMakeLists.txt file.
1. Configure the build.
1. Build the project.

### Add the segger-toolchain-cmake package to the project

* Open the existing project directory (we'll call it `$ProjectDir`).
* Download and extract the segger-toolchain-cmake package into `$ProjectDir/segger-toolchain-cmake` (or add it as a git submodule).

### Add SEGGER Toolchain-specific files to the project

The target startup code, runtime initialization, and linker script are specific to the compiler and linker.
To switch from gcc (or another toolchain) to the SEGGER Toolchain, they need to be updated.
Generic files to start with are available in the Embedded Studio installation directory (we'll call it `$StudioDir`).
Pre-configured, target-specific files are available in Embedded Studio's CPU Support Packages.

* Create a new folder `$ProjectDir/segger`
* Copy `$StudioDir/samples/Cortex_M_Startup.s` to `$ProjectDir/segger/Cortex_M_Startup.s`
* Copy `$StudioDir/samples/SEGGER_THUMB_Startup.s` to `$ProjectDir/segger/SEGGER_THUMB_Startup.s`
* Copy `$StudioDir/samples/SEGGER_Flash.icf` to `$ProjectDir/segger/SEGGER_Flash.icf`
* Copy the folder `$ProjectDir/segger-toolchain-cmake/Samples/cmake_modules` to `$ProjectDir/cmake_modules`
* Copy/edit files under `$ProjectDir/cmake_modules/Platform/` and adapt to project needs.

### Adapt the project CMakeLists.txt file

* Set *CMAKE_SYSTEM_PROCESSOR* at the start of CMakeLists.txt (before `project()`)
  ```cmake
  set(CMAKE_SYSTEM_PROCESSOR arm-cortex-m4)
  ```
* Add the copied `cmake_modules` folder to CMAKE_MODULE_PATH so that CMake can find the `Platform` folder in it.
  ```cmake
  if(NOT "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules" IN_LIST CMAKE_MODULE_PATH)
    list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules")
  endif()
  ```
* Add the startup code to the project sources and remove existing ones
  ```cmake
  # Add the additional device-specific files to the source-list for the application.
  target_sources(${PROJECTNAME} PRIVATE
    segger/Cortex_M_Startup.s
    segger/SEGGER_THUMB_Startup.s
  )
  ```
* Set the appropriate compiler and assembler flags.
  For a switch from gcc to the SEGGER Toolchain no change might be necessary.
  Otherwise, use the provided Platform files (described below) as reference.
* Add the linker script as a dependency to re-link the project on changes
  ```cmake
  set_target_properties(${PROJECTNAME} PROPERTIES
    LINK_DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/segger/SEGGER_Flash.icf
  )
  ```
* Set the linker options for the [SEGGER Linker](https://doc.segger.com/UM20005_Linker.html)
  ```cmake
  target_link_options(${PROJECTNAME} PRIVATE
    -T${CMAKE_CURRENT_SOURCE_DIR}/segger/SEGGER_Flash.icf
    # Set memory regions of device
    -Wl,--add-region:FLASH1=0x00100000@0x00000000
    -Wl,--add-region:RAM1=0x00010000@0x20000000
    # Application-specific configuration
    -Wl,--defsym=__STACKSIZE__=2048
    -Wl,--defsym=__HEAPSIZE__=2048
    # I/O Configuration
    -Wl,--defsym=__SEGGER_RTL_vfprintf=__SEGGER_RTL_vfprintf_int_nwp
    -Wl,--defsym=__SEGGER_RTL_vfscanf=__SEGGER_RTL_vfscanf_int
    -io=rtt
    # Entry Point
    -e_start
    # Optional settings
    --full-section-headers
    --merge-sections
    --merge-strings
    --map-text
    --map-standard
    --no-outline
    --relax
    --no-springboard
    --no-tail-merge
    --tp-model=auto
    --list-all-undefineds
  )
  ```

### Configure the build

* Create a build directory (we'll use `$ProjectDir/build-segger`) and cd into it:
  ```bash
  mkdir build-segger && cd build-segger
  ```
* Call cmake with the "Ninja Multi-Config" generator and the segger-toolchain file:
  ```bash
  cmake -G "Ninja Multi-Config" --toolchain ../segger-toolchain-cmake/segger-toolchain.cmake -S ..
  ```
### Build the project

* Call cmake with the configuration to build.
  ```bash
  cmake --build . --config Debug
  ```

## Details about relevant variables
- Mandatory variables for SEGGER toolchain:
  - ```CMAKE_TOOLCHAIN_FILE```: path to *segger-toolchain.cmake*
  - ```CMAKE_SYSTEM_PROCESSOR```: name of the target System-Processor. See [below](#segger-specific-rules-for-cmake_system_processor) for important rules.
- Optional variables for SEGGER toolchain:
  - ```SEGGER_TOOLCHAIN_PKG_ROOT=%DIRPATH%```: Path to the root of the SEGGER Embedded Studio installation directory. If this is not specified, segger-toolchain.cmake shall attempt to detect it automatically.
  - ```SEGGER_USE_GCC=1```: Use the SEGGER compiler in GNU/gcc mode.
  - ```SEGGER_USE_GNU_LINKER=1```: (Only in combination with SEGGER_USE_GCC=1) For GNU/gcc mode, use the GNU linker instead of the SEGGER linker. If SEGGER_USE_GNU_LINKER is not set, the SEGGER linker will be used (even if using the SEGGER toolchain in GNU/gcc mode).

## SEGGER specific rules for *CMAKE_SYSTEM_PROCESSOR*
The CMake documentation states that the value of `CMAKE_SYSTEM_PROCESSOR` *"can be chosen freely"*. However for SEGGER toolchain support to work properly, `CMAKE_SYSTEM_PROCESSOR` must be of the form:\
```<supported_cpu_family>-<free_string>```\
where:
- `<supported_cpu_family>` = any of the cpu family names as found under the gcc folder of SEGGER Embedded Studio installation location.\
At the time of writing, the following cpu family names are valid: `aarch64, andes, arm, corev, riscv32`
- `<free_string>` = an arbitrary string without whitespace.
### Rationale
- The SEGGER CMake integration uses the `<supported_cpu_family>` part of `CMAKE_SYSTEM_PROCESSOR` to find the correct binutils that correspond to the CPU family in use.
- CMake uses `<supported_cpu_family>-<free_string>` to find the so-called "Platform" files which can be located at a project-defined location.

#### Examples of valid values for CMAKE_SYSTEM_PROCESSOR:
- `arm-any_nice_string`
- `riscv32-another_nice_string`
- `arm-cortex-m4` : (assuming <project_folder>/cmake_modules has been added to `CMAKE_MODULE_PATH`) this will cause Cmake to process the files:
  - `<project_folder>/cmake_modules/Platform/Generic-SEGGERClang-C-arm-cortex-m4.cmake` for details for compiling C language files
  - `<project_folder>/cmake_modules/Platform/Generic-SEGGERClang-CXX-arm-cortex-m4.cmake` for details for compiling C++ language files
  - `<project_folder>/cmake_modules/Platform/Generic-SEGGERClang-ASM-arm-cortex-m4.cmake` for details for compiling assembler language files

<details><summary>Background and implementation:</summary>

#### Official Specification:
From the [CMake Documentation](https://cmake.org/cmake/help/book/mastering-cmake/chapter/Cross%20Compiling%20With%20CMake.html) we read this:
> CMAKE_SYSTEM_PROCESSOR\
>     This variable is optional; it sets the processor or hardware name of the target system. It is used in CMake for one purpose, to load the `${CMAKE_SYSTEM_NAME}-COMPILER_ID-${CMAKE_SYSTEM_PROCESSOR}.cmake` file. This file can be used to modify settings such as compiler flags for the target. You should only have to set this variable if you are using a cross-compiler where each target needs special build settings. The value can be chosen freely, so it could be, for example, i386 IntelPXA255, or MyControlBoardRev42.

**NOTE:** the `COMPILER_ID` as mentioned in the documentation is actually a combination of the compiler ID + compiler language. Therefore for GNU/gcc `COMPILER_ID` may be `GNU-C` and for SEGGER `COMPILER_ID` may be `SEGGERClang-C` or `SEGGERClang-CXX` etc.
#### Implementation:
We can use the behaviour of CMake as documented above to move cpu/architecture specific toolchain settings into the files within this folder.\
By appending this folder to `CMAKE_MODULE_PATH` in the project `CMakeLists.txt`, we make sure that
these files get processed accordingly during cmakes compiler detection phase.
```CMake
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules")
```
</details>

## Details on using CMake in VSCode
CMake and the SEGGER toolchain can be used with VS Code and its CMake Tools extension.  
This example shows how to build the *MltCfgApp* sample project with the SEGGER toolchain in VSCode.
The description is based on the steps described in the previous chapter.

### Methods:
There are various ways through which the CMake Tools VSCode Extension can be made aware of the SEGGER toolchain:
* Using `cmake-kits.json`
* Using `CMakePresets.json`

Of these two methods, **using CMakePresets.json is recommended and shall be described here**, since it is the most versatile approach and is also used by Cmake natively. For details on this approach, please refer to https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html.

#### Using `CMakePresets.json`:
The *MltCfgApp* sample project comes with a preconfigured `CMakePresets.json` file [CMakePresets.json](./Samples/MltCfgApp/CMakePresets.json), that demonstrates the ability to build the project for 3 different embedded targets (ARM based and RISCV based) as well as for building the same application to run natively on the Windows host (using MSVC).  
The most important aspects for enabling the SEGGER toolchain using presets is to define 2 variables using the `cacheVariables` fields:
* `CMAKE_TOOLCHAIN_FILE`
* `CMAKE_SYSTEM_PROCESSOR`

These 2 variables must be defined according to the description provided [in the above sections](#details-about-relevant-variables). One may make use of the macro-expansions provided by Cmake (see https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html#macro-expansion).
#### Example excerpt from the `CMakePresets.json` file, showing how to define a preset for Cortex M4:
```json
  {
    "hidden": true,
    "name": "segger_base",
    "description": "Base config-preset for SEGGER toolchain",
    "generator": "Ninja Multi-Config",
    "binaryDir": "${sourceDir}/preset_output/build/${presetName}",
    "cacheVariables": {
      "CMAKE_TOOLCHAIN_FILE": "${sourceDir}/../../segger-toolchain.cmake"
    },
    "environment": {
      "PATH": "$penv{PATH};C:\\Program Files (x86)\\Microsoft Visual Studio\\2019\\Professional\\Common7\\IDE\\CommonExtensions\\Microsoft\\CMake\\Ninja"
    }
  },
  {
    "inherits": "segger_base",
    "name": "segger_arm_cortex_m4",
    "displayName": "segger_arm_cortex_m4",
    "description": "Build for cortex M4 with SEGGER compiler.",
    "cacheVariables": {
      "CMAKE_SYSTEM_PROCESSOR": "arm-cortex-m4"
    }
  }
```
### Debugging using [SEGGER J-Link](https://www.segger.com/products/debug-probes/j-link/) or [SEGGER J-Trace](https://www.segger.com/products/debug-probes/j-trace/):
Debugging with a debugger probe is supported by using the VS-Code extension "cortex-debug" by Marus25 (see [Prerequisites for VS-Code](#prerequisites)).\
For debugging with VSCode, a corresponding launch/debug configuration is required (in the `launch.json` file).
A detailed description of using cortex-debug and J-Link can be found at these locations:
* https://github.com/Marus/cortex-debug/wiki
* https://github.com/Marus/cortex-debug/wiki/J-Link-Specific-Configuration
* https://github.com/Marus/cortex-debug/wiki/SEGGER-RTT-support

#### Example excerpt from the `launch.json` file, showing how to start a debug session with J-Link connected to the SEGGER emPower eval board:

```JSON
{
  "name": "Debug with JLink",
  "cwd": "${workspaceFolder}",
  "executable": "${command:cmake.launchTargetPath}",
  "request": "launch",
  "type": "cortex-debug",
  "servertype": "jlink",
  "serverpath": "C:/Program Files/SEGGER/JLink/JLinkGDBServerCL.exe",
  "device": "MK66FX1M0xxx18",
  "interface": "jtag",
  "showDevDebugOutput": "none",
  "gdbPath": "C:/tools/gcc-arm-none-eabi-10.3-2021.10/bin/arm-none-eabi-gdb.exe",
  "liveWatch": {
    "enabled": true,
    "samplesPerSecond": 4
  },
  "breakAfterReset": false,
  "runToEntryPoint": "main",
  "rttConfig": {
    "enabled": true,
    "address": "auto",
    "decoders": [
      {
        "label": "jlink_rtt_console",
        "port": 0,
        "type": "console",
        "inputmode": "disabled",
        "prompt": "jlink_rtt:0> ",
        "noclear": false
      }
    ]
  }
}
```
