Last edited one day ago

How to port StarterAppM33TD demonstration to a new board

(Redirected from How to port StarterApp to a new board)
Applicable for STM32MP21x lines, STM32MP25x lines

Info white.png Trusted domain applicability
This article is only applicable to M33-TD flavor More info green.png of STM32MP2 series
Warning DB.png Important
In this article, the STM32MP215F-DK Discovery kit More info green.png is taken as example. Nevertheless, the same approach applies to other boards based on the same microprocessor family (for example STM32MP257F-EV1 Evaluation board More info green.png) and to custom boards. All instructions and structures are oriented to **STM32CubeIDE**.

1. Introduction[edit | edit source]

The STM32MP2 series embedded software, with M33-TD flavor More info green.png, provides a secure and flexible foundation for system bring-up:

  • MCUboot for secure boot and firmware update.
  • Trusted Firmware-M (TF-M)[1] for secure services on the secure side (CM33-S).
  • A nonsecure FreeRTOS application on CM33-NS acting as an application manager and supervising the high-performance Arm® Cortex®-A35 processor.

Within this embedded software, the STM32CubeMP2 package provides several CM33-NS applications dedicated to the M33-TD flavor More info green.png. Among them, the Template_StarterApp_M33TD project is a bare-minimum CM33-NS application that uses the common stack located under Utilities/M33TD_NSAppCore to implement the nonsecure application manager.

This article explains how to port the minimal CM33-NS application manager (based on Template_StarterApp_M33TD) to your own board using STM32CubeIDE. The focus is on the board-specific parts: drivers, BSP, and configuration, while reusing the common nonsecure stack and the TF-M / SoC-level startup/system files provided for the target device.

Info white.png Information
This page focuses on the bare-minimum nonsecure application for M33-TD flavor More info green.png based on the Template_StarterApp_M33TD project. For a detailed description of all features and for full-featured reference projects, refer to the README[2] files provided in the STM32CubeMP2 Package.

2. Prerequisites[edit | edit source]

Before starting, ensure that:

This STM32CubeMP2 firmware package includes

    • the Template_StarterApp_M33TD project,
    • the Utilities/M33TD_NSAppCore stack.
  • The Trusted Firmware-M (TF-M) repository must be cloned under the STM32CubeMPU firmware tree, typically as: Firmware/Middlewares/Third_Party/trusted-firmware-m.

3. Minimal NS application manager overview[edit | edit source]

The minimal nonsecure application manager for M33-TD flavor More info green.png is implemented in the STM32CubeMP2 package by the Template_StarterApp_M33TD project. It:

  • Runs FreeRTOS in the CM33 nonsecure environment.
  • Uses the common stack under Utilities/M33TD_NSAppCore as the NS application manager core.
  • Provides a minimal but complete set of services for system bring-up:
    • Logging via a central logger task.
    • Arm® Cortex®-A35 lifecycle control via a remote processor task (start, monitor, recovery) through TF-M services.
    • SCMI notification and event handling.
    • Watchdog supervision.
    • A simple LED activity task that toggles a user LED to show system liveness.
Info white.png Information
For a detailed description of all tasks and behavior in the minimal application, refer to:
  • The Template_StarterApp_M33TD[3] README in the STM32CubeMP2 Package.
  • The M33TD_NSAppCore README[4] under Utilities/M33TD_NSAppCore, which documents the task catalog, configuration options, and integration flow.

4. Architecture overview (with M33TD_NSAppCore)[edit | edit source]

The nonsecure M33-TD application is built around the Trusted Firmware-M secure boot chain and the common NS application stack:

MCUboot  ->  TF-M (CM33-S)  ->  CM33-NS FreeRTOS application
                                   |
                                   v
                             NSCoreApp_Init()
                                   |
                                   v
                        M33TD_NSAppCore tasks
           (Logger / SCMI Mgr / RemoteProc / Wdg Monitor / UserApp)
                                   |
                  +----------------+----------------+
                  |                                 |
                  v                                 v
         Board-specific drivers             TF-M secure services

Key points:

  • The common stack is located under Utilities/M33TD_NSAppCore and provides:
    • Task implementations in App/Src.
    • Task APIs in App/Inc.
    • Driver interfaces in Includes.
    • Shared helpers in Common.
    • Configuration templates in Config.
  • The Template_StarterApp_M33TD project:
    • Adds these sources to the CM33-NS build.
    • Provides board-specific driver implementations under the application project.
    • Provides configuration headers to enable and size the tasks.
    • Calls NSCoreApp_Init() from the nonsecure main.c once the RTOS kernel is initialized.

In the bare-minimum configuration, the application manager typically enables:

  • Logger task.
  • SCMI manager task.
  • Remote processor task.
  • Watchdog monitor task.
  • User application task (LED heartbeat).

Other tasks such as button monitor, OpenAMP, or display handling can remain disabled in this minimal profile.

5. Step-by-step porting guide[edit | edit source]

5.1. Create a project for your board[edit | edit source]

Use the Template_StarterApp_M33TD project from STM32CubeMP2[5] as the starting point:

  1. In the STM32CubeMP2 package, locate the template for your reference board, for example:
    Firmware/Projects/STM32MP215F-DK/Templates/Template_StarterApp_M33TD
  2. Copy this directory to a new location for your custom board (for example, a new folder with your board name).
  3. In STM32CubeIDE:
    • Import the CM33 nonsecure project (for example, Template_StarterApp_M33TD_CM33_NonSecure) as an existing project in your workspace.
    • Optionally rename the imported project to reflect your board name.
  4. Ensure that the associated TF-M project (secure CMake project) remains accessible if you plan to build Secure and nonsecure from the same workspace.
Info white.png Information

For more information on importing, building and debugging M33-TD projects in STM32CubeIDE, refer to: How_to_import,_build_and_debug_STM32CubeMP2_StarterAppM33TD_demonstration_in_STM32CubeIDE.

5.2. Common stack usage in the project[edit | edit source]

The template project already integrates the common nonsecure stack from Utilities/M33TD_NSAppCore into the CM33-NS build:

  • Source files from the stack are added to the project, typically from:
    • Utilities/M33TD_NSAppCore/App/Src
    • Optionally Utilities/M33TD_NSAppCore/Common if features like FaultMgr are used.
  • Include directories are configured so that the application can use:
    • Utilities/M33TD_NSAppCore/App/Inc
    • Utilities/M33TD_NSAppCore/Includes
    • Utilities/M33TD_NSAppCore/Common

These files provide the generic nonsecure application manager logic and tasks and are designed to be board-agnostic for a given device family. During porting, you typically reuse this stack in the same way and focus on adapting:

  • Board-specific driver implementations.
  • Board BSP and pinout.
  • Configuration for task enablement and features.
Info white.png Information

The M33TD_NSAppCore README in the STM32CubeMP2 package provides a detailed description of:

  • The tasks implemented by the stack.
  • The helper modules (such as FaultMgr).
  • The recommended CMake/IDE integration patterns.

5.3. Implement board-specific drivers[edit | edit source]

The common stack calls board-specific functionality through driver interfaces declared in headers located under Utilities/M33TD_NSAppCore/Includes. The Template_StarterApp_M33TD project provides corresponding implementations in an AppDriver directory under the CM33-NS application.

For a new board, the main porting work consists of adapting these AppDriver files to match your hardware:

  • Verify where the template project keeps the AppDriver sources, typically under:
Application/User/FREERTOS/M33TD_NSAppCore/AppDriver/ (exact path may vary slightly depending on the project version).
  • For the minimal NS application manager, the following driver categories are commonly used:
    • Logging output driver: configures the UART (or another physical interface) used for console logging.
    • LED driver: configures and toggles the user LED used by the liveness task.
    • Any additional low-level hooks referenced by the RemoteProc/SCMI/Watchdog tasks if present in the AppDriver layer.

Adapt these implementations to your custom board:

  • Update GPIO port, pins, and alternate functions according to your board schematics.
  • Update UART instance, clock enables, and pin configuration for the logging interface.
  • Check any use of the resource manager (ResMgr) to match the resources you want to use on your board.

Board-level initialization in the BSP (for example, in stm32mp2xx_hal_msp.c and in the board BSP files under Drivers/BSP) must be consistent with these AppDriver implementations.

5.4. Configure tasks and features[edit | edit source]

Task enablement, stack sizes, priorities and various feature flags are controlled by configuration macros. These macros are defined in project headers that are derived from the templates provided by the stack (under Utilities/M33TD_NSAppCore/Config).

In the Template_StarterApp_M33TD project, these configuration headers are already present under the CM33-NS application folder and are set for a bare-minimum configuration.

You can control the task and feature macros in two main ways:

  • By defining or overriding the macros in the preprocessor settings of the CM33-NS project in STM32CubeIDE (Project Properties → C/C++ Build → Settings → MCU GCC Compiler → Preprocessor).
  • By editing the project configuration headers, where the macros are collected and mapped to the stack defaults.

In the default Template_StarterApp_M33TD configuration, the minimal NS application manager enables:

Task / Feature Default state in Template_StarterApp_M33TD
Logger task enabled
SCMI manager task enabled
Remote processor task enabled
Watchdog monitor task enabled
User application task (LED activity) enabled
Button monitor task disabled
OpenAMP task disabled
Display task disabled

Additional macros, such as the remote processor auto-start option (for example, controlling whether the Cortex-A35 is started automatically at CM33-NS boot), can also be set through:

  • The project preprocessor definitions in STM32CubeIDE, or
  • The appropriate configuration header, following the mapping described in the templates.

The configuration templates in Utilities/M33TD_NSAppCore/Config, the M33TD_NSAppCore README, describe all available options and their default values.

5.5. CM33 nonsecure bootstrap[edit | edit source]

The nonsecure main.c performs device initialization, optional early board initialization, then starts the RTOS and the nonsecure application manager:

Typical flow:

main.c entry
   |
   v
HAL_Init() / system clock configuration
   |
   v
(Optional) Board-level initialization for custom application logic
   |
   v
osKernelInitialize()
   |
   v
NSCoreApp_Init()   (initializes the NS application manager stack and tasks)
   |
   v
osKernelStart()    (starts the FreeRTOS scheduler)

When porting to your board, keep in mind:

  • System clock configuration and any early BSP initialization must be aligned with your hardware requirements.
  • The optional "Board-level initialization" step in main.c is intended for your own CM33-NS application logic (for example, extra peripherals or user-specific initialization) that needs to be performed before the RTOS starts.
  • The initialization of NS application manager tasks and their board-specific drivers is handled inside NSCoreApp_Init(), which calls into the M33TD_NSAppCore stack and the AppDriver layer.

5.6. Build and run[edit | edit source]

After adapting drivers and configuration:

  1. Build the TF-M secure firmware for your board and desired boot mode, following the TF-M build instructions and using the appropriate external device tree and CMake options (as described in the TF-M documentation and Template_StarterApp_M33TD README).
  2. In STM32CubeIDE, select the CM33-NS project configuration that is set up to:
    • Build the nonsecure CM33 application.
    • Invoke the postbuild utility to assemble and sign the combined TF-M S/NS image (for example, a configuration with a suffix like _StarterApp_build_ns).
  3. Build the CM33-NS project. At the end of the build, the postbuild step typically generates the binaries in a bin/ folder, including:
    • The CM33-NS application binary.
    • The signed TF-M S/NS combined binary.
    • The DDR PHY firmware binary.
    • The BL2 bootloader binary.
  4. Integrate these binaries into the OpenSTLinux image tree and TSV as described in the Template_StarterApp_M33TD README and flashing wiki.
  5. Flash the image to your board and reset or power-cycle the platform.

On the Arm® Cortex®-M33 UART console, you should observe:

  • TF-M boot messages on the secure side.
  • nonsecure startup messages from the M33-NS application (log prefix [NS]).
  • Messages from the watchdog monitor and remote processor task.
  • The Arm® Cortex®-A35 being started (if configured).
  • The LED on your board toggling periodically to indicate system liveness.

6. Minimal project structure (after porting)[edit | edit source]

After porting Template_StarterApp_M33TD to your custom board, a minimal STM32CubeIDE project structure may look as follows (adapted to the M33TD_NSAppCore-based design):

YourStarterApp_CM33_NonSecure
├── Application
│   ├── Startup
│   │   └── startup_stm32mp2xx_m33.s       <-- Startup file (unchanged for a given SoC)
│   └── User
│       ├── Core
│       │   └── Src
│       │       ├── main.c
│       │       ├── stm32mp2xx_hal_msp.c
│       │       ├── stm32mp2xx_hal_timebase_tim.c
│       │       └── stm32mp2xx_it.c
│       └── FREERTOS
│           ├── App
│           │   └── (Application-level sources and configuration)
│           └── M33TD_NSAppCore
│               └── AppDriver
│                   ├── logger-related drivers
│                   ├── LED-related drivers
│                   └── other board drivers used by the NS application manager
├── TFM
│   └── App
│       ├── tfm_ioctl_cpu_api.c
│       ├── tfm_ns_interface.c
│       ├── tfm_platform_api.c
│       ├── tfm_scmi_api.c
│       └── tfm_tz_psa_ns_api.c
├── Drivers
│   ├── BSP
│   │   └── (Custom board BSP files only)
│   └── CMSIS
│       ├── system_stm32mp2xx_m33_ns.c     <-- System file (unchanged for a given SoC)
│       └── STM32MP2xx_HAL_Driver
├── Middlewares
│   └── FreeRTOS
├── Utilities
│   └── M33TD_NSAppCore
│       ├── App
│       ├── Includes
│       ├── Common
│       └── Config
└── (Trusted Firmware-M as submodule or reference)

Notes:

  • The startup file and system file are device-level files and are usually identical for all boards based on the same STM32MP2 series.
  • Board-specific adaptations are typically confined to:
    • BSP files under Drivers/BSP.
    • M33TD_NSAppCore driver implementations under the AppDriver directory.
    • Application configuration and any board-related logic in the application sources under Application/User.

7. Project cleanup and best practices[edit | edit source]

After a successful bring-up on your custom board, consider the following clean-up actions:

  • Keep the project aligned with the template structure and avoid copying unrelated demonstration code that is not used.
  • Keep board-specific code clearly separated:
    • BSP under Drivers/BSP.
    • M33TD_NSAppCore driver implementations under the AppDriver directory.
  • Centralize task and feature configuration in the dedicated configuration headers and/or project preprocessor settings, rather than spreading defines across multiple files.
  • If you later enable additional tasks (for example button monitor or OpenAMP), add the corresponding driver implementations and configuration in a structured way, reusing the patterns established by the template project.

8. Code examples[edit | edit source]

8.1. Logger output driver initialization[edit | edit source]

The minimal NS application manager uses a logging task to print messages to a serial console. The underlying UART configuration is performed in the logger output driver.

Example (extracted and simplified from a logger_output_driver.c implementation):

#include "logger_output_driver.h"
#include "main.h"
#include <stdio.h>

COM_InitTypeDef COM_Init;

/**
  * @brief  Initialize the logger output.
  * @retval None
  */
void logger_output_init(void)
{
  /* Initialize UART for logging */
  if (ResMgr_Request(RESMGR_RESOURCE_RIFSC, RESMGR_RIFSC_UART4_ID) == RESMGR_STATUS_ACCESS_OK)
  {
    COM_Init.BaudRate   = 115200;
    COM_Init.WordLength = UART_WORDLENGTH_8B;
    COM_Init.StopBits   = UART_STOPBITS_1;
    COM_Init.Parity     = UART_PARITY_NONE;
    COM_Init.HwFlowCtl  = UART_HWCONTROL_NONE;

    /* Initialize and select COM port associated with the current core */
    BSP_COM_Init(COM_VCP_CM33, &COM_Init);
    BSP_COM_SelectLogPort(COM_VCP_CM33);
  }
}

When porting to your board:

  • Adapt the UART resource (for example, RESMGR_RIFSC_UARTx_ID).
  • Ensure that BSP COM initialization matches your board's UART instance and pinout.
  • Keep the function signature and naming consistent with the logger output driver interface used by M33TD_NSAppCore.

8.2. LED driver instance for liveness[edit | edit source]

The user application task in the minimal profile typically toggles a LED periodically as a liveness indicator. The LED control is provided by a driver instance that exposes function pointers for initialization and basic operations.

Example (extract from a led_driver.c implementation):

#include "led_driver.h"
#include "main.h"

/* Private function prototypes -----------------------------------------------*/
static void LedDriver_Init(void);
static void LedDriver_DeInit(void);
static void LedDriver_Toggle(LedId_t led_id);
static void LedDriver_On(LedId_t led_id);
static void LedDriver_Off(LedId_t led_id);

/* Exported driver instance --------------------------------------------------*/
/**
  * @brief LED driver instance.
  */
LedDriverTypeDef led_driver =
{
  .init       = LedDriver_Init,
  .deinit     = LedDriver_DeInit,
  .led_toggle = LedDriver_Toggle,
  .led_on     = LedDriver_On,
  .led_off    = LedDriver_Off,
};

/* Private functions ---------------------------------------------------------*/

/**
  * @brief  Initialize the LED driver and underlying BSP LED(s).
  */
static void LedDriver_Init(void)
{
  BSP_LED_Init(LED3); /* Adapt LED3 to the LED used on your board */
}

/**
  * @brief  De-initialize the LED driver and underlying BSP LED(s).
  */
static void LedDriver_DeInit(void)
{
  BSP_LED_DeInit(LED3);
}

/**
  * @brief  Toggle the state of the specified LED.
  */
static void LedDriver_Toggle(LedId_t led_id)
{
  switch (led_id)
  {
    case LED_ID_3:
      BSP_LED_Toggle(LED3);
      break;

    /* Add more cases for other LEDs if needed */
    default:
      break;
  }
}

/**
  * @brief  Turn on the specified LED.
  */
static void LedDriver_On(LedId_t led_id)
{
  switch (led_id)
  {
    case LED_ID_3:
      BSP_LED_On(LED3);
      break;

    default:
      break;
  }
}

/**
  * @brief  Turn off the specified LED.
  */
static void LedDriver_Off(LedId_t led_id)
{
  switch (led_id)
  {
    case LED_ID_3:
      BSP_LED_Off(LED3);
      break;

    default:
      break;
  }
}

When porting to your board:

  • Adapt the LED identifiers and the BSP LED macros (LED3, LED_ID_3) to match your board.
  • Ensure that the LedDriverTypeDef instance and function signatures follow the interface defined by led_driver.h used in M33TD_NSAppCore.

8.3. NSCoreApp bootstrap in main.c[edit | edit source]

The CM33-NS main.c initializes the device and RTOS, then calls the nonsecure application manager bootstrap function.

Example:

int main(void)
{
  /* HAL and system initialization */
  HAL_Init();
  SystemClock_Config();

  /* Optional: board-level initialization for custom application logic */
  Board_Init();

  /* Initialize the RTOS kernel */
  osKernelInitialize();

  /* Initialize the nonsecure application manager stack (M33TD_NSAppCore) */
  NSCoreApp_Init();

  /* Start the RTOS scheduler */
  osKernelStart();

  /* The program should not reach here */
  for (;;)
  {
  }
}

This pattern ensures that the common NS application manager stack is initialized after the OS kernel and before the scheduler starts running tasks.

9. Troubleshooting[edit | edit source]

If issues are encountered during porting or bring-up, consider the following points:

  • No Arm® Cortex®-M33 logs on the UART console
    • Check that the logging task is enabled in the configuration.
    • Verify the UART instance, pins and clocks in the logging-related driver and BSP.
    • Confirm that the UART resource (if using ResMgr) is requested correctly.
  • LED does not toggle
    • Confirm that the LED driver initialization and toggle functions use the correct GPIO port and pin for your board.
    • Ensure that the user application task is enabled in the configuration.
    • Verify that the LED GPIO clock is enabled.
  • Arm® Cortex®-A35 is not started or not managed
    • Check that the remote processor task is enabled.
    • Verify that the TF-M secure firmware has been built correctly with the appropriate device tree and CMake options.
    • Review the remote processor auto-start option and SCMI configuration as documented in the READMEs.
  • Build errors in AppDriver files
    • Make sure that function signatures and include paths match the driver interfaces declared in Utilities/M33TD_NSAppCore/Includes.
    • Confirm that all required header files are included in your AppDriver sources.
  • Unexpected behavior after enabling additional tasks (button, OpenAMP, display)
    • Ensure that all related drivers and configuration options have been added and that the dependencies described in the M33TD_NSAppCore README are satisfied.

10. Extending the minimal setup[edit | edit source]

Once the minimal NS application manager is running correctly on your custom board, you may want to enable additional features provided by the common stack, such as:

  • Button monitoring and classification.
  • OpenAMP-based RPMsg communication with the Cortex-A35.
  • Display control and Linux-driven display interactions.

These features are controlled by configuration macros (similar to those used for the minimal tasks). For each additional feature:

  • Enable the corresponding task in the configuration (via project preprocessor settings and/or configuration headers).
  • Add or adapt the required board-specific drivers (for example, IPCC/mailbox drivers for OpenAMP, button drivers, display drivers).
  • Use the full-featured reference projects and the M33TD_NSAppCore README as guidance for typical profiles, dependencies, and end-to-end behavior.

11. References[edit | edit source]

  1. Refer trusted-firmware-m
  2. For STM32MP215F-DK Refer README.md
  3. . For STM32MP215F-DK refer Project Template_StarterApp_M33TD
  4. Refer M33TD_NSAppCore Utility README.md
  5. Refer Latest STM32CubeMP2 Firmware Package