| This article is only applicable to M33-TD flavor |
1. Introduction[edit | edit source]
The STM32MP2 series embedded software, with M33-TD flavor
, 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
. 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.
This page focuses on the bare-minimum nonsecure application for M33-TD flavor 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:
- STM32CubeIDE is installed on your development PC.
- Download Latest cube Firmware package for STM32CubeMP2 from https://www.st.com/en/embedded-software/stm32cubemp2.html on your local machine.
This STM32CubeMP2 firmware package includes
- the
Template_StarterApp_M33TDproject, - the
Utilities/M33TD_NSAppCorestack.
- the
- 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
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_NSAppCoreas 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.
For a detailed description of all tasks and behavior in the minimal application, refer to:
|
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_NSAppCoreand provides:- Task implementations in
App/Src. - Task APIs in
App/Inc. - Driver interfaces in
Includes. - Shared helpers in
Common. - Configuration templates in
Config.
- Task implementations in
- 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 nonsecuremain.conce 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:
- In the STM32CubeMP2 package, locate the template for your reference board, for example:
Firmware/Projects/STM32MP215F-DK/Templates/Template_StarterApp_M33TD
- Copy this directory to a new location for your custom board (for example, a new folder with your board name).
- 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.
- Import the CM33 nonsecure project (for example,
- Ensure that the associated TF-M project (secure CMake project) remains accessible if you plan to build Secure and nonsecure from the same workspace.
|
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/Commonif features like FaultMgr are used.
- Include directories are configured so that the application can use:
Utilities/M33TD_NSAppCore/App/IncUtilities/M33TD_NSAppCore/IncludesUtilities/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.
|
The
|
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.cis 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:
- 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).
- 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).
- 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.
- Integrate these binaries into the OpenSTLinux image tree and TSV as described in the Template_StarterApp_M33TD README and flashing wiki.
- 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
AppDriverdirectory. - Application configuration and any board-related logic in the application sources under
Application/User.
- BSP files under
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
AppDriverdirectory.
- BSP under
- 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
LedDriverTypeDefinstance and function signatures follow the interface defined byled_driver.hused 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.
- Make sure that function signatures and include paths match the driver interfaces declared in
- 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]
- ↑ Refer trusted-firmware-m
- ↑ For STM32MP215F-DK Refer README.md
- ↑ . For STM32MP215F-DK refer Project Template_StarterApp_M33TD
- ↑ Refer M33TD_NSAppCore Utility README.md
- ↑ Refer Latest STM32CubeMP2 Firmware Package