FreeRTOS 10.3.1 Integration with SEGGER SystemView on VSCode

Hey there! Just thought I’d drop a guide on how to do this integration since I didn’t see any guides on how to do it pain-free. PLEASE bump this up so people on google can find this quickly. Thank you!

# SEGGER J-Link + SystemView Setup — M7 (STM32H745 CM7) / FreeRTOS 10.3.1 / CMake+Ninja

This is a CMake/Ninja STM32CubeMX-managed project, rooted like this:

```

/

├── .vscode/

| └── launch.json

├── CM7/

│ ├── CMakeLists.txt

│ ├── mx-generated.cmake

│ ├── stm32h745xx_flash_CM7.ld

│ ├── Core/

│ │ ├── Inc/

| | | ├── FreeRTOSConfig.h

| | | └── main.h

│ │ └── Src/

| | └── freertos.c

│ └── build//

├── Middlewares/

│ └── Third_Party/

| ├── FreeRTOS/

│ └── SystemView/

│ ├── Config/

│ | ├── Global.h

| | ├── SEGGER_RTT_Conf.h

| | ├── SEGGER_RTT_ConfDefaults.h

│ | ├── SEGGER_SYSVIEW_Conf.h

│ | └── SEGGER_SYSVIEW_Config_FreeRTOS.c

| ├── Sample/

| | ├── SEGGER_SYSVIEW_FreeRTOS.c

| | └── SEGGER_SYSVIEW_FreeRTOS.h

| └── Segger/

| ├── SEGGER_RTT_ASM_ARMv7M.S

| ├── SEGGER_RTT.c

| ├── SEGGER_RTT.h

| ├── SEGGER_SYSVIEW_ConfDefaults.h

| ├── SEGGER_SYSVIEW_Int.h

| ├── SEGGER_SYSVIEW.c

| ├── SEGGER_SYSVIEW.h

| └── SEGGER.h

└── Outputs/M7/M7.elf ← final build artifact (see note below)

```

Hardware: SEGGER J-Link BASE, S/N XXXXXXXX, via TC2030-IDC → ARM20-CTX adapter, into the board’s SWD pins.

## 1. Hardware Setup

1. Tag-Connect TC2030-IDC cable → J-Link BASE’s ARM20-CTX adapter (keyed, one orientation).

2. Clip the Tag-Connect end onto the board’s SWD pads: `VTref`, `SWDIO`, `SWCLK`, `GND`, and `nRESET` if broken out.

3. Power the board from its own supply — confirm `VTref` reads 3.3V on the J-Link once powered.

4. Make sure nothing else is contending for the SWD bus (e.g. an onboard ST-LINK footprint next to Tag-Connect).

## 2. Install software (Windows — matches your `C:\Users\\` setup)

### 2a. J-Link Software and Documentation Pack

1. Go to Segger’s website under its jlink option and download **“J-Link Software and Documentation pack”** for Windows (64-bit) — it’s the same installer whether you have a standalone probe or an onboard one.

2. Run the installer. Accept the default install path (`C:\Program Files\SEGGER\JLink_V`) unless you have a reason not to. (I named mine JLink since I had an older version).

3. During install, let it register the J-Link DLL and add itself to `PATH` (there’s a checkbox for this — leave it checked so `JLinkGDBServerCL.exe` and `JLink.exe` are callable from any terminal). If not, add them to path yourself by pressing Win > Edit Environment Variables > Environment Variables > Double click ‘Path’ in the System variables dropdown box > click ‘New’ to add a new path > Add the path to your JLink directory (C:\Program Files\SEGGER\JLink for me)

4. Plug in the J-Link BASE (S/N XXXXXXXX) via USB now if you haven’t. Windows should install the driver automatically from the pack you just installed, if not, be sure to do that; check **Device Manager → Universal Serial Bus devices** for “J-Link driver” with no yellow warning icon.

5. Sanity check from a terminal:

```

JLink.exe

```

It should connect to the J-Link BASE and print its serial number/firmware version at the `J-Link>` prompt (type `q` to quit). If it doesn’t find the probe, unplug/replug and re-check Device Manager before going further.

Also ensure that in your environment variables, the path variable includes C:\Program Files\SEGGER\JLink so JLink may be called from any system directory

If that doesn’t work, most likely the JLink probe needs its firmware updated. Open the JLink Configurator app and ensure it is connected over usb to your machine. Select

it from the list of available devices and right click to replace its current firmware with the most recent version.

If that still doesn’t work, some USB ports may not work, so swap to a different one if you are able, or reconfigure drivers per-port

### 2b. SEGGER SystemView application

1. Under Segger’s systemview web link→ download **SystemView** for Windows.

2. Run the installer (default path `C:\Program Files\SEGGER\SystemView_V`). This is the GUI you’ll use later to view the trace — separate from the J-Link pack above.

3. Launch it once after install just to confirm it opens — you don’t need to configure anything yet.

### 2c. SystemView Target Sources (the actual C source you add to your firmware)

1. Go to Segger’s SystemView github→ git clone **SystemView**

2. Go to Segger’s RTT github → git clone **RTT**

This is just source code sitting in Program Files — installing SystemView does **not** touch your firmware or your repo in any way. In step 4 you’ll manually copy files out of this folder into `Middlewares/Third_Party/SystemView/` in your own repo, where they get compiled as part of your normal CMake build like any other source file.

> **FreeRTOS version note:** your project is on FreeRTOS Kernel V10.3.1. SystemView’s target-sources package ships a few different `Sample/FreeRTOS*` folders for different kernel generations (older ones exist for pre-V9 kernels using the “legacy” trace macro API). You want **`Sample/FreeRTOSV10/`** specifically — it targets the trace-macro API used by V9 through V10.x, which covers 10.3.1 exactly. Don’t reach for anything named `FreeRTOSV8` or similar; those use an older, incompatible macro set. Nothing else about being on 10.3.1 specifically (vs. a later 10.x point release) requires special handling — the include we’re adding in step 3 works as-is. DO NOT PATCH!! It will work as long as your FreeRTOS version matches any version of 10.X.X

## 3. FreeRTOSConfig.h

Your `CM7/Core/Inc/FreeRTOSConfig.h` should have these lines that SystemView needs:

```c

#define configUSE_TRACE_FACILITY 1

#define configGENERATE_RUN_TIME_STATS 1

#define configMAX_PRIORITIES (56)

#define configTIMER_TASK_PRIORITY (2)

#define configMAX_SYSCALL_INTERRUPT_PRIORITY (configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY << (8 - configPRIO_BITS))

```

You only need to **add two defines and one include**, in the existing (currently empty) `/* USER CODE BEGIN Defines */` block near the bottom of the file:

```c

/* USER CODE BEGIN Defines */

#define INCLUDE_xTaskGetIdleTaskHandle 1

#define INCLUDE_pxTaskGetStackStart 1

#include “SEGGER_SYSVIEW_FreeRTOS.h”

/* USER CODE END Defines */

```

That include is what actually wires up FreeRTOS’s `trace*` macros (`traceTASK_SWITCHED_IN`, `traceISR_ENTER`, `traceQUEUE_SEND`, etc.) that `tasks.c`/`queue.c` call internally. It must be visible from this file, and it must come **after** `configMAX_SYSCALL_INTERRUPT_PRIORITY` is defined above it (it already will be, since that’s earlier in the file).

## 4. Add SystemView sources to the tree

Create a new top-level middleware folder alongside FreeRTOS, matching your existing convention:

```

Middlewares/

└── Third_Party/

├── FreeRTOS/

└── SystemView/

    ├── Config/

    |   ├── Global.h

    |   ├── SEGGER_RTT_Conf.h

    |   ├── SEGGER_RTT_ConfDefaults.h

    |   ├── SEGGER_SYSVIEW_Conf.h

    |   └── SEGGER_SYSVIEW_Config_FreeRTOS.c

    ├── Sample/

    |   ├── SEGGER_SYSVIEW_FreeRTOS.c

    |   └── SEGGER_SYSVIEW_FreeRTOS.h

    └── Segger/

        ├── SEGGER_RTT_ASM_ARMv7M.S

        ├── SEGGER_RTT.c

        ├── SEGGER_RTT.h

        ├── SEGGER_SYSVIEW_ConfDefaults.h

        ├── SEGGER_SYSVIEW_Int.h

        ├── SEGGER_SYSVIEW.c

        ├── SEGGER_SYSVIEW.h

        └── SEGGER.h

```

Copy these out of the extracted `SystemView` package’s `Config/`, `SEGGER/`, `SYSVIEW/`, `Sample/FreeRTOSV10/`, `SEGGER/`, and and `RTT` package’s `Config` and `RTT` directories respectively.

## 5. Configure `SEGGER_SYSVIEW_ConfDefaults.h` for CM7

`Middlewares/Third_Party/SystemView/Config/SEGGER_SYSVIEW_Conf.h`:

```c

// Base RAM address for compressing IDs — AXI SRAM start on H745.

// Cross-check against stm32h745xx_flash_CM7.ld’s RAM origin for your D1/AXI region.

#define SEGGER_SYSVIEW_ID_BASE 0x24000000

```

> Try ignoring this step at first, the default should be fine. If it is not working, you can open `stm32h745xx_flash_CM7.ld` and check the RAM region your `.data`/`.bss` actually lands in — if the CM7 app’s RAM origin isn’t `0x24000000` (e.g. it uses `RAM_D1` or a different offset), update `SEGGER_SYSVIEW_ID_BASE` like the above to match. Being slightly off just costs a couple of extra bytes per RTT packet — not a functional problem.

## 6. Wire SystemView into `main.h` and ‘freertos.c’

In `CM7/Core/Inc/main.h`, inside the existing `/* USER CODE BEGIN Includes */` block:

```c

/* USER CODE BEGIN Includes */

/* Other includes */

#include “SEGGER_SYSVIEW_FreeRTOS.h”

/* USER CODE END Includes */

```

Then in some task in `CM7/Core/Src/freertos.c `, add in the sysview hook logic:

```c

void SysviewSwitchTask(void *argument)

{

SEGGER_SYSVIEW_Conf();

/\* Infinite loop \*/

for ( ;; )

{

    if (SEGGER_SYSVIEW_IsStarted())

    {

        SEGGER_SYSVIEW_Start();

        SCB_CleanDCache();

        \__DSB();

        \__ISB();

    }

    else

    {

        SEGGER_SYSVIEW_Stop();

    }

    vTaskDelay(pdMS_TO_TICKS(250));

}

}

```

Edit the copied `SEGGER_SYSVIEW_Config_FreeRTOS.c` (in `Middlewares/Third_Party/SystemView/FreeRTOSV10/Config/`) and set:

```c

#define SYSVIEW_APP_NAME “M7”

#define SYSVIEW_DEVICE_NAME “STM32H745BI_M7”

```

## 7. Add SystemView to the CMake build

There are two places you may add the include directories and source files. Firstly, the most direct solution is to edit `CM7/mx-generated.cmake`:

```cmake

# Add include paths

target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC

../Middlewares/Third_Party/SystemView/SEGGER

../Middlewares/Third_Party/SystemView/Config

../Middlewares/Third_Party/SystemView/Sample/FreeRTOSV10

)

# Add sources to executable

target_sources(${CMAKE_PROJECT_NAME} PUBLIC

../Middlewares/Third_Party/SystemView/Config/SEGGER_SYSVIEW_Config_FreeRTOS.c

../Middlewares/Third_Party/SystemView/Sample/FreeRTOSV10/SEGGER_SYSVIEW_FreeRTOS.c

../Middlewares/Third_Party/SystemView/SEGGER/SEGGER_RTT.c

../Middlewares/Third_Party/SystemView/SEGGER/SEGGER_SYSVIEW.c

../Middlewares/Third_Party/SystemView/SEGGER/SEGGER_RTT_ASM_ARMv7M.S

)

```

However, that file is CubeMX-owned and gets regenerated on every new addition. Instead, you may add the same edits above into `CM7/CMakeLists.txt` in this structure:

```cmake

add_executable(${CMAKE_PROJECT_NAME})

include(“mx-generated.cmake”)

# Add include paths

target_include_directories(${CMAKE_PROJECT_NAME} PUBLIC

../Middlewares/Third_Party/SystemView/SEGGER

../Middlewares/Third_Party/SystemView/Config

../Middlewares/Third_Party/SystemView/Sample/FreeRTOSV10

)

# Add sources to executable

target_sources(${CMAKE_PROJECT_NAME} PUBLIC

../Middlewares/Third_Party/SystemView/Config/SEGGER_SYSVIEW_Config_FreeRTOS.c

../Middlewares/Third_Party/SystemView/Sample/FreeRTOSV10/SEGGER_SYSVIEW_FreeRTOS.c

../Middlewares/Third_Party/SystemView/SEGGER/SEGGER_RTT.c

../Middlewares/Third_Party/SystemView/SEGGER/SEGGER_SYSVIEW.c

../Middlewares/Third_Party/SystemView/SEGGER/SEGGER_RTT_ASM_ARMv7M.S

)

```

Ensure that you place the target include directories and sources **below** add_executable and include(“mx-generated.cmake”).

## 8. Build

You may skip this step if you do not use VSCode.

### 8a. Launch Configuration in VSCode

To properly debug as well as run systemview with your system, you will need to add a launch configuration that included the JLink SystemView hardware:

```json

{

"version": "0.2.0",

"configurations": \[

    {

        "name": "Debug CM7 - J-Link Base",

        "cwd": "${workspaceFolder}",

        "type": "cortex-debug", // EDIT: Attach or debug

        "executable": "${workspaceFolder}/Outputs/M7/M7.elf",

        "request": "launch",

        "servertype": "jlink",

        "interface": "swd",

        "device": "STM32H745BI_M7",

        "jlinkSerialNo": *<S/N* *Here>*, // EDIT: Binds to your physical box identity

        "runToEntryPoint": "main",

        "svdFile": "/opt/st/STM32CubeCLT_1.17.0/STMicroelectronics_CMSIS_SVD/STM32H745_CM7.svd",

        "armToolchainPath": "/opt/st/STM32CubeCLT_1.17.0/GNU-tools-for-STM32/bin",

        "gdbPath": "/opt/st/STM32CubeCLT_1.17.0/GNU-tools-for-STM32/bin/arm-none-eabi-gdb",

        "windows": {

            "serverpath": *<Path* *To* *JLinkGDBServerCL.exe* *Here>*, // EDIT: Opens the JLink server path to your physical box

            "svdFile": "C://st/STM32CubeCLT_1.17.0/STMicroelectronics_CMSIS_SVD/STM32H745_CM7.svd",

            "armToolchainPath": "C://st/STM32CubeCLT_1.17.0/GNU-tools-for-STM32/bin",

            "gdbPath": "C://st/STM32CubeCLT_1.17.0/GNU-tools-for-STM32/bin/arm-none-eabi-gdb"

        },

        "v1": false,

        "preLaunchTask": *<Prelaunch* *Task* *Here>*, // EDIT: Build or otherwise here before attaching/debugging

        "liveWatch": {

            "enabled": true,

            "samplesPerSecond": 4

        }

    },

\]

}

```

Ensure that you populate the EDIT fields with your own information.

### 8b. Launching The System

From `CM7/`, use either the module created in `8a` or the existing presets:

```bash

cmake --preset Debug

cmake --build --preset Debug

```

Or if you normally drive it through STM32CubeIDE’s CMake integration, just re-run **Build** — CubeIDE will re-invoke `cmake --build` under the hood using the same preset.

Confirm any build artifact lands at:

```

CM7/Outputs/M7/M7.elf

```

(computed from `CMakePresets.json`'s `binaryDir = CM7/build/` plus the `RUNTIME_OUTPUT_DIRECTORY ../../Outputs/M7` set in `CMakeLists.txt`.)

Then just allow the system to either build, flash, and run, or attach to the current board state.

## 9. Debug/flash via J-Link

You may skip this step if your system uses VSCode.

### Option A — STM32CubeIDE, CMake project import

If you’ve imported this as a CMake project in CubeIDE (**File → Import → C/C++ → Existing CMake Project**, pointing at the repo root or `CM7/`):

1. **Run → Debug Configurations… → GDB SEGGER J-Link Debugging** → New.

2. **Main tab**: C/C++ Application → browse to `CM7/Outputs/M7/M7.elf`.

3. **Debugger tab**: J-Link GDB Server executable → your installed `JLinkGDBServerCL`. Device: `STM32H745BI_M7`. Interface: SWD. Speed: 4000 kHz.

4. **Debug** — flashes and halts at `main()`.

### Option B — standalone JLinkGDBServer + arm-none-eabi-gdb (works regardless of IDE)

```bash

JLinkGDBServerCL -device STM32H745ZI_M7 -if SWD -speed 4000 -select USB=52006601

```

In another terminal:

```bash

arm-none-eabi-gdb CM7/Outputs/M7/M7.elf

(gdb) target remote localhost:2331

(gdb) load

(gdb) monitor reset

(gdb) continue

```

Either way, flashing writes to the region starting at `0x08020000` (per your `CM7_APP_START` in `CMakeLists.txt` and the SREC crop step) — confirm your J-Link/GDB flash download isn’t overwriting the bootloader living below that address; the `.ld` script should already constrain this correctly if the SREC post-build step works today.

## 10. Connect SystemView and record

1. Launch **SystemView**.

2. **Target → Recorder Configuration**:

  • Recorder: **J-Link**, Connection: USB, S/N `52006601`.

  • Device: `STM32H745BI_M7`, Interface: SWD, Speed: 4000 kHz.

  • RTT Control Block: **Auto Detection**, or if that fails, navigate to your CM7 directory’s `M7.map` (or similar) and search _SEGGER_RTT. The address to the left of the line found is the RTT control block that is required in the **Address** option in SystemView. Point SystemView at that address so it can resolve `_SEGGER_RTT`.

3. **Target → Start Recording** (F5).

You should see the board’s FreeRTOS tasks (whatever `freertos.c`/`MX_FREERTOS_Init()` creates), the idle task, and ISR activity populate the Timeline immediately.

If it stays empty:

- Breakpoint on `SEGGER_SYSVIEW_IsStarted()` in `freertos.c` to confirm it’s actually reached. The segger configuration should only run **after** FreeRTOS has initialized so that it may hook onto it correctly.

- Double-check the `#include “SEGGER_SYSVIEW_FreeRTOS.h”` line survived in main.h — CubeMX regeneration only preserves things inside `USER CODE` markers, and this one is, so it should be safe across `.ioc` regens, but worth a sanity check after any regeneration.

## Quick reference — files touched

| File | Change |

|—|—|

| `CM7/Core/Inc/FreeRTOSConfig.h` | Added `INCLUDE_xTaskGetIdleTaskHandle`, `INCLUDE_pxTaskGetStackStart`, `#include “SEGGER_SYSVIEW_FreeRTOS.h”` in the existing `USER CODE BEGIN Defines` block |

| `Middlewares/Third_Party/SystemView/Config/SEGGER_SYSVIEW_Conf.h` | Modified `SEGGER_SYSVIEW_ID_BASE` |

| `CM7/Core/Src/main.h` | `#include “SEGGER_SYSVIEW_FreeRTOS.h”` in existing `USER CODE Includes` block |

| `CM7/CMakeLists.txt` | Multiple lines: `target_include_directories` and `target_sources` after `add_executable` and `include(“mx-generated.cmake”)` |

Once it’s recording cleanly, worth doing next: bump `SEGGER_SYSVIEW_RTT_BUFFER_SIZE` up from the 1KB default in `SEGGER_SYSVIEW_Conf.h` if you see overflow markers under load, and drop `SEGGER_SYSVIEW_Print()` calls at board state-machine transitions (cell balancing, fault trips, wake-line events) so they show up as annotated markers alongside the task/ISR trace.

Thank you for sharing!