Skip to main content
Associate
June 30, 2026
Solved

How to Use STL(Self Test Library)

  • June 30, 2026
  • 8 replies
  • 242 views

 

/*
* Stl_Scheduler.c
*
* Created on: Jun 25, 2026
* Author: User
*/

#include <stl_user_api.h>
#include "stl_scheduler.h"
#include "main.h"
#include "app_threadx.h"
#include "APP_Log.h"

extern TIM_HandleTypeDef htim15;

/* RAM & FLASH Tests definitions */
#define FLASH_SECTION_SIZE 1024U
#define RAM_SECTION_SIZE 128U

#ifdef STL_DISABLE_RAM_BCKUP_BUF
#define RAM_BACKUP_BUFFER_SIZE 0
#else
#define RAM_BACKUP_BUFFER_SIZE 32
#endif

// STM32H523
#define TEST_RAM_START_ADDR (0x20000000U + RAM_BACKUP_BUFFER_SIZE)
#define TEST_ROM_START_ADDR 0x08000000U
//#define TEST_ROM_END_ADDR 0x0801A3FF
//#define TEST_ROM_END_ADDR 0x0801A00FU
#define TEST_ROM_END_ADDR 0x0801A3FFU
//#define TEST_ROM_END_ADDR 0x0807F7FFU


/*
#if (defined (__GNUC__) && !defined (__CC_ARM) && !defined (__ARMCC_VERSION))
extern void *_edata_load;
#define TEST_ROM_END_ADDR ( (uint32_t)&_edata_load + 3)
#endif
*/

// Runtime RAM test: 8 sections x 128 bytes = 1024 bytes
#define TEST_RAM_SECTION_NB_RUN 8 /* only 8 RAM sections are tested during runtime. */

// STM32H523 SRAM = 272 KB 0x20000000 + 0x44000 - 1 = 0x20043FFF
#define TEST_RAM_END_ADDR_FULL 0x20043FFFU

/////////////////////////////////////////////////////////////
/* Value of the system clock frequency at run time in Hz */
#define SYSTCLK_AT_RUN (uint32_t)(250000000uL)
/* Value of the Internal LSI oscillator in Hz */
#define LSI_Freq ((uint32_t)32000uL)
/* CLK frequency above this limit considered as harmonics in case of HSE */
#define CLK_LimitHigh(fcy) ((uint32_t)(((fcy)/LSI_Freq)*4u*5u)/4u) /* (Value + 25%) */
/* CLK frequency below this limit considered as sub-harmonics in case of HSE */
#define CLK_LimitLow(fcy) ((uint32_t)(((fcy)/LSI_Freq)*4u*3u)/4u) /* (Value - 25%) */
/* if SYSCK is derived from HSI, the upper fixed clock limits can be set more severe
e.g., ...*2u*6u)/5 resp. ...*2u*4u)/5 (~ value +/- 20%) but such a severe restriction
can require including some adaptive flow to compensate possible temperature drift
of HSI by making acceptable window defined by these limits variable in time */

/* uncomment following lines when simulating artificial failing of particular modules
during execution of either full or run test */
/* #define ARTI_FAILING_CPU_TM */
/* #define ARTI_FAILING_FLASH_TM */
//#define ARTI_FAILING_RAM_TM
/* #define ARTI_FAILING_CLK_TM */
/* #define ARTI_FAILING_DURING_FULL_TEST */


/* variables to keep user configuration and status of all the test modules
- can be handled as local and/or changed dynamically inside the STL_RunStep procedure
- set of global variables is applied here to keep overall overview when debug STL */
static STL_TmStatus_t StlCpuTm1LStatus;
static STL_TmStatus_t StlCpuTm7Status;
static STL_TmStatus_t StlCpuTmCBStatus;
static STL_TmStatus_t StlFlashStatus;
static STL_TmStatus_t StlRamStatus;
static STL_TmStatus_t StlClkStatus;

/* following variables have to be static due to separation of configuration and run procedures */
/* Flash & RAM configurations: */
static STL_MemSubset_t FlashSubsetRunTime;
static STL_MemSubset_t RamSubsetRunTime;
static STL_MemConfig_t FlashConfigRunTime;
static STL_MemConfig_t RamConfigRunTime;


/* internal variables for clock cross check measurement */
volatile uint16_t tmpCC1_last;
volatile uint32_t PeriodValue;
volatile uint32_t PeriodValueInv;
volatile uint32_t LSIPeriodFlag;

/* structure to handle artificial failing */
#if defined(ARTI_FAILING_CPU_TM) || defined(ARTI_FAILING_FLASH_TM) || defined(ARTI_FAILING_RAM_TM)
STL_ArtifFailingConfig_t ArtifFailing = {{ STL_PASSED, STL_PASSED, STL_PASSED }, STL_PASSED, STL_PASSED };
#endif /* ARTI_FAILING */


extern uint32_t __stl_ram_test_start__;
extern uint32_t __stl_ram_test_end__;

static STL_MemSubset_t MyRamSubsetRunTime = {
.StartAddr = (uint32_t)&__stl_ram_test_start__,
.EndAddr = (uint32_t)&__stl_ram_test_end__ - 1U,
.pNext = NULL
};

static STL_MemConfig_t MyRamConfigRunTime = {
.NumSectionsAtomic = UINT32_MAX,
.pSubset = &MyRamSubsetRunTime
};


void FailSafe_Handler(uint32_t fail_code)
{
/* Here, user has to add an action to setup & keep the application in safe state.
The fail_code input parameter together with TM dedicated user status value
can be used to identify detected issue and adopt specific action */
while (1)
{
HAL_GPIO_WritePin(LED_ON2_GPIO_Port, LED_ON2_Pin, GPIO_PIN_SET); //RED
HAL_Delay(100U);
HAL_GPIO_WritePin(LED_ON2_GPIO_Port, LED_ON2_Pin, GPIO_PIN_RESET); //RED
HAL_Delay(100U);
Log_Printf("FailSafe_Handler=%u", fail_code);
/*
if(fail_code < DEF_PROG_OFFSET)
{
HAL_Delay(1900);
}
else
{
HAL_Delay(3900);
}
*/
}
}


void Thread_FailSafe_Handler(uint32_t fail_code)
{
/* Here, user has to add an action to setup & keep the application in safe state.
The fail_code input parameter together with TM dedicated user status value
can be used to identify detected issue and adopt specific action */
while (1)
{
HAL_GPIO_WritePin(LED_ON2_GPIO_Port, LED_ON2_Pin, GPIO_PIN_SET); //RED
tx_thread_sleep(100U);
HAL_GPIO_WritePin(LED_ON2_GPIO_Port, LED_ON2_Pin, GPIO_PIN_RESET); //RED
tx_thread_sleep(100U);

Log_Printf("Thread_FailSafe_Handler=%u", fail_code);
/*
if(fail_code < DEF_PROG_OFFSET)
{
HAL_Delay(1900);
}
else
{
HAL_Delay(3900);
}
*/
}
}



static STL_Status_t STL_InitClock_Xcross_Measurement(void)
{
TIM_IC_InitTypeDef sConfigIC = {0};

/* TIMx Peripheral clock enable */
// __TIM15_CLK_ENABLE();
__HAL_RCC_CSI_ENABLE();
while (__HAL_RCC_GET_FLAG(RCC_FLAG_CSIRDY) == RESET)
{
}

__HAL_RCC_TIM15_CLK_ENABLE();


/* Configure the NVIC for TIM12 */
HAL_NVIC_SetPriority(TIM15_IRQn, 0, 0);
HAL_NVIC_EnableIRQ(TIM15_IRQn);

htim15.Instance = TIM15;
htim15.Init.Prescaler = 0;
htim15.Init.CounterMode = TIM_COUNTERMODE_UP;
htim15.Init.Period = 65535;
htim15.Init.ClockDivision = TIM_CLOCKDIVISION_DIV1;
htim15.Init.RepetitionCounter = 0;
htim15.Init.AutoReloadPreload = TIM_AUTORELOAD_PRELOAD_DISABLE;
if (HAL_TIM_Base_Init(&htim15) != HAL_OK)
{
return (STL_KO);
}
if (HAL_TIM_IC_Init(&htim15) != HAL_OK)
{
return (STL_KO);
}
sConfigIC.ICPolarity = TIM_INPUTCHANNELPOLARITY_RISING;
sConfigIC.ICSelection = TIM_ICSELECTION_DIRECTTI;
sConfigIC.ICPrescaler = TIM_ICPSC_DIV4;
sConfigIC.ICFilter = 0;
if (HAL_TIM_IC_ConfigChannel(&htim15, &sConfigIC, TIM_CHANNEL_1) != HAL_OK)
{
return (STL_KO);
}
if (HAL_TIMEx_TISelection(&htim15, TIM_TIM15_TI1_CSI_128, TIM_CHANNEL_1) != HAL_OK)
{
return (STL_KO);
}
/* Start the TIM Input Capture measurement in interrupt mode */
if(HAL_TIM_IC_Start_IT(&htim15, TIM_CHANNEL_1) != HAL_OK)
{
return (STL_KO);
}
return (STL_OK);
}

void STL_Init(void)
{

/* FLASH test Config
*****************
In this example, single continuous area (subset) is tested under unique configuration setting.
User can apply wider set of subsets tested sequentially to cover separated memory areas
as well as different configurations to be applied for selected subset(s) - see UM */
FlashSubsetRunTime.StartAddr = TEST_ROM_START_ADDR;
FlashSubsetRunTime.EndAddr = TEST_ROM_END_ADDR;
FlashSubsetRunTime.CrcTableIdx = STL_CRC_TABLE_NO_TZ;
FlashSubsetRunTime.pNext = NULL;
FlashConfigRunTime.pSubset = &FlashSubsetRunTime;
FlashConfigRunTime.NumSectionsAtomic = 1; /* split test into sections of 1kB */

/* RAM test Config
***************
In this example, single continuous area (subset) is tested under unique configuration setting.
User can apply wider set of subsets tested sequentially to cover separated memory areas
as well as different configurations to be applied for selected subset(s) - see UM */
RamSubsetRunTime.StartAddr = TEST_RAM_START_ADDR;
RamSubsetRunTime.EndAddr = TEST_RAM_START_ADDR + TEST_RAM_SECTION_NB_RUN * RAM_SECTION_SIZE - 1;
RamSubsetRunTime.pNext = NULL;
RamConfigRunTime.pSubset = &RamSubsetRunTime;
RamConfigRunTime.NumSectionsAtomic = 1; /* split test into sections of 128 bytes */
/* RamConfig.NumSectionsAtomic = (2 * TEST_RAM_SECTION_NB); */ /* - use this setting for one shot */


/* test status initialization */
StlCpuTm1LStatus = STL_NOT_TESTED;
StlCpuTm7Status = STL_NOT_TESTED;
StlCpuTmCBStatus = STL_NOT_TESTED;
StlFlashStatus= STL_NOT_TESTED;
StlRamStatus= STL_NOT_TESTED;

if (STL_SCH_Init() != STL_OK)
{
FailSafe_Handler(SCH_ERR_CODE + DEF_PROG_OFFSET);
}


// Clock test Config
if(STL_InitClock_Xcross_Measurement() != STL_OK)
{
FailSafe_Handler(CXM_ERR_CODE + DEF_PROG_OFFSET);
}
// Wait for two subsequent LSI periods measurements to perform initial crosscheck computation
LSIPeriodFlag = 0u;
while (LSIPeriodFlag == 0u)
{

}
LSIPeriodFlag = 0u;
while (LSIPeriodFlag == 0u)
{

}

}
STL_Status_t STL_RunClockTest(STL_TmStatus_t *clk_sts)
{
STL_Status_t tst_res;

*clk_sts = STL_PARTIAL_PASSED;
/* next line can produce compilation warning due to accessing of two volatile
variables making single result value integrity pair. The pair is changed
simultaneously exclusively at TIM16 IRQ and its integrity is verified here
before the clock measurement is compared if it fits within expected range */
if (((PeriodValue ^ PeriodValueInv) == 0xFFFFFFFFuL)\
&& (LSIPeriodFlag == 0xAAAA5555u) )
{
#ifndef ARTI_FAILING_CLK_TM
*clk_sts = STL_PASSED;
if (PeriodValue < CLK_LimitLow(SYSTCLK_AT_RUN))
{
*clk_sts = STL_FAILED; /* Sub-harmonics: HSE -25% below expected */
}
if (PeriodValue > CLK_LimitHigh(SYSTCLK_AT_RUN))
{
*clk_sts = STL_FAILED; /* Harmonics: HSE +25% above expected */
}
#else
*clk_sts = STL_FAILED;
#endif /* ARTI_FAILING_CLK_TM */

/* clear flag here to ensure the latest LSI measurement result will be taken into account at next check */
LSIPeriodFlag = 0u;
tst_res = STL_OK;
}
else
{
tst_res = STL_KO; /* Clock measurement flow error */
}
return(tst_res);
}

void STL_RunFullTest(void)
{
/* Example of complete device self-test
************************************
This function executes all the provided CPU, flash, RAM, and clock test modules in this order. Test
of both the memories is performed in one shot in their entirety here. The function should be called
prior to the main application entry, but can also be re-called by the application at any later point.
Configuration structures for handling tests of the memories are declared locally, therefore they
cannot be referenced outside of the function which is supposed to be executed as a whole.
*/

/********************/
/* CPU Test modules */
/********************/

#if defined(ARTI_FAILING_CPU_TM) && defined(ARTI_FAILING_DURING_FULL_TEST)
/* Artificial failing feature -
when activated, it forces the STL outputs to predefined values */
ArtifFailing.aCpuTmStatus[0] = STL_PASSED;
ArtifFailing.aCpuTmStatus[1] = STL_PASSED;
ArtifFailing.aCpuTmStatus[2] = STL_FAILED;
STL_SCH_StartArtifFailing(&ArtifFailing);
#endif /* ARTI_FAILING_CPU_TM */

/* CPU TM1L */
if (STL_SCH_RunCpuTM1L(&StlCpuTm1LStatus) != STL_OK)
{
FailSafe_Handler(TM1L_ERR_CODE + DEF_PROG_OFFSET);
}
if (StlCpuTm1LStatus != STL_PASSED)
{
FailSafe_Handler(TM1L_ERR_CODE);
}
/* CPU TM7 */

if (STL_SCH_RunCpuTM7(&StlCpuTm7Status) != STL_OK)
{
FailSafe_Handler(TM7_ERR_CODE + DEF_PROG_OFFSET);
}
if (StlCpuTm7Status != STL_PASSED)
{
FailSafe_Handler(TM7_ERR_CODE);
}
/* CPU TMCB */
if (STL_SCH_RunCpuTMCB(&StlCpuTmCBStatus) != STL_OK)
{
FailSafe_Handler(TMCB_ERR_CODE + DEF_PROG_OFFSET);
}
if (StlCpuTmCBStatus != STL_PASSED)
{
FailSafe_Handler(TMCB_ERR_CODE);
}

#if defined(ARTI_FAILING_CPU_TM) && defined(ARTI_FAILING_DURING_FULL_TEST)
STL_SCH_StopArtifFailing();
#endif /* ARTI_FAILING_CPU_TM */


/*********************/
/* FLASH Test Module */
/*********************/

#if defined(ARTI_FAILING_FLASH_TM) && defined(ARTI_FAILING_DURING_FULL_TEST)
/* forced STL_FAILED status simulates the TM configuration error and
STL_NOT_TESTED status simulates TM run error */
ArtifFailing.FlashTmStatus = STL_FAILED;
STL_SCH_StartArtifFailing(&ArtifFailing);
#endif /* ARTI_FAILING_FLASH_TM */

/* The following configuration tests the entire program in the flash, in one shot */
STL_MemSubset_t FlashSubsetFullTest = {
.StartAddr = TEST_ROM_START_ADDR,
.EndAddr = TEST_ROM_END_ADDR,
.CrcTableIdx = STL_CRC_TABLE_NO_TZ,
.pNext = NULL
};
STL_MemConfig_t FlashConfigFullTest = {
.NumSectionsAtomic = UINT32_MAX,
.pSubset = &FlashSubsetFullTest
};
if (STL_SCH_InitFlash(&StlFlashStatus) != STL_OK)
{
FailSafe_Handler(TMF_ERR_CODE + DEF_PROG_OFFSET);
}

if (STL_SCH_ConfigureFlash(&StlFlashStatus, &FlashConfigFullTest) != STL_OK)
{
FailSafe_Handler(TMF_ERR_CODE + DEF_PROG_OFFSET);
}
else if (StlFlashStatus != STL_NOT_TESTED)
{
FailSafe_Handler(TMF_ERR_CODE);
}

/*
STL_Status_t ret;

ret = STL_SCH_RunFlashTM(&StlFlashStatus);

if (ret != STL_OK)
{
volatile STL_Status_t debug_ret = ret;
volatile STL_TmStatus_t debug_flash_status = StlFlashStatus;
FailSafe_Handler(TMF_ERR_CODE + DEF_PROG_OFFSET);
}
*/

if (STL_SCH_RunFlashTM(&StlFlashStatus) != STL_OK)
{
FailSafe_Handler(TMF_ERR_CODE + DEF_PROG_OFFSET);
}
else if (StlFlashStatus != STL_PASSED)
{
FailSafe_Handler(TMF_ERR_CODE);
}

StlFlashStatus = STL_NOT_TESTED;

#if defined(ARTI_FAILING_FLASH_TM) && defined(ARTI_FAILING_DURING_FULL_TEST)
STL_SCH_StopArtifFailing();
#endif /* ARTI_FAILING_FLASH_TM */


/********************/
/* RAM Test module */
/********************/

#if defined(ARTI_FAILING_RAM_TM) && defined(ARTI_FAILING_DURING_FULL_TEST)
/* forced STL_FAILED status simulates the TM configuration error and
STL_NOT_TESTED status simulates TM run error */
ArtifFailing.RamTmStatus = STL_NOT_TESTED;
STL_SCH_StartArtifFailing(&ArtifFailing);
#endif /* ARTI_FAILING_RAM_TM */

/* The following configuration tests the entire user-defined range of the RAM used by the program */
/* RAM subset: */
STL_MemSubset_t RamSubsetFullTest = {
.StartAddr = TEST_RAM_START_ADDR,
.EndAddr = TEST_RAM_END_ADDR_FULL,
.pNext = NULL
};
STL_MemConfig_t RamConfigFullTest = {
.NumSectionsAtomic = UINT32_MAX,
.pSubset = &RamSubsetFullTest
};

if (STL_SCH_InitRam(&StlRamStatus) != STL_OK)
{
FailSafe_Handler(TMR_ERR_CODE + DEF_PROG_OFFSET);
}
if (STL_SCH_ConfigureRam(&StlRamStatus, &RamConfigFullTest) != STL_OK)
{
FailSafe_Handler(TMR_ERR_CODE + DEF_PROG_OFFSET);
}
else if (StlRamStatus != STL_NOT_TESTED)
{
FailSafe_Handler(TMR_ERR_CODE);
}
if (STL_SCH_RunRamTM(&StlRamStatus) != STL_OK)
{
FailSafe_Handler(TMR_ERR_CODE + DEF_PROG_OFFSET);
}
else if (StlRamStatus != STL_PASSED)
{
FailSafe_Handler(TMR_ERR_CODE);
}
StlRamStatus = STL_NOT_TESTED;

#if defined(ARTI_FAILING_RAM_TM) && defined(ARTI_FAILING_DURING_FULL_TEST)
STL_SCH_StopArtifFailing();
#endif /* ARTI_FAILING_RAM_TM */

/************/
/* Clock TM */
/************/
StlClkStatus = STL_NOT_TESTED;
if (STL_RunClockTest(&StlClkStatus) != STL_OK)
{
FailSafe_Handler(CLK_ERR_CODE + DEF_PROG_OFFSET);
}
if (StlClkStatus != STL_PASSED)
{
FailSafe_Handler(CLK_ERR_CODE);
}
}



void STL_RunStep(void)
{
/* Example of the test sequence execution
**************************************
In this example, a unique sequence performing all the tests in one shot is applied.
Note it is fully upon user to define order and repetition frequency of all the API calls.
The same sequence of the calls can be repeated regularly or few different sequences can be
applied at different time slots. All the tests can be repeated without any limitation
except for clock test which can not be called earlier than 8 LSI periods to prepare new clock
computation result in between two comparisons (otherwise the clock API call fails). */

/********************/
/* CPU Test modules */
/********************/

#ifdef ARTI_FAILING_CPU_TM
/* Artificial failing feature -
when activated, it forces the STL outputs to predefined values */
ArtifFailing.aCpuTmStatus[0] = STL_PASSED;
ArtifFailing.aCpuTmStatus[1] = STL_PASSED;
ArtifFailing.aCpuTmStatus[2] = STL_FAILED;
STL_SCH_StartArtifFailing(&ArtifFailing);
#endif /* ARTI_FAILING_CPU_TM */

/* CPU TM1L */
if (STL_SCH_RunCpuTM1L(&StlCpuTm1LStatus) != STL_OK)
{
Thread_FailSafe_Handler(TM1L_ERR_CODE + DEF_PROG_OFFSET);
}
if (StlCpuTm1LStatus != STL_PASSED)
{
Thread_FailSafe_Handler(TM1L_ERR_CODE);
}
/* CPU TM7 */
#if 0
if (STL_SCH_RunCpuTM7(&StlCpuTm7Status) != STL_OK)
{
Thread_FailSafe_Handler(TM7_ERR_CODE + DEF_PROG_OFFSET);
}
if (StlCpuTm7Status != STL_PASSED)
{
Thread_FailSafe_Handler(TM7_ERR_CODE);
}
#endif
/* CPU TMCB */
if (STL_SCH_RunCpuTMCB(&StlCpuTmCBStatus) != STL_OK)
{
Thread_FailSafe_Handler(TMCB_ERR_CODE + DEF_PROG_OFFSET);
}
if (StlCpuTmCBStatus != STL_PASSED)
{
Thread_FailSafe_Handler(TMCB_ERR_CODE);
}

#ifdef ARTI_FAILING_CPU_TM
STL_SCH_StopArtifFailing();
#endif /* ARTI_FAILING_CPU_TM */


/************/
/* Flash TM */
/************/

#ifdef ARTI_FAILING_FLASH_TM
/* forced STL_FAILED status simulates the TM configuration error and
STL_NOT_TESTED status simulates TM run error */
ArtifFailing.FlashTmStatus = STL_FAILED;
STL_SCH_StartArtifFailing(&ArtifFailing);
#endif /* ARTI_FAILING_FLASH_TM */

if (StlFlashStatus == STL_NOT_TESTED)
{
/* Init & configure Flash TM */
if (STL_SCH_InitFlash(&StlFlashStatus) != STL_OK)
{
Thread_FailSafe_Handler(TMF_ERR_CODE + DEF_PROG_OFFSET);
}
if (STL_SCH_ConfigureFlash(&StlFlashStatus, &FlashConfigRunTime) != STL_OK)
{
Thread_FailSafe_Handler(TMF_ERR_CODE + DEF_PROG_OFFSET);
}
else if (StlFlashStatus != STL_NOT_TESTED)
{
Thread_FailSafe_Handler(TMF_ERR_CODE);
}
}
/* run partial atomic test */

if (STL_SCH_RunFlashTM(&StlFlashStatus) != STL_OK)
{
Thread_FailSafe_Handler(TMF_ERR_CODE + DEF_PROG_OFFSET);
}
if (StlFlashStatus != STL_PARTIAL_PASSED)
{
if (StlFlashStatus == STL_PASSED)
{
// test completed successfully - to be restarted
StlFlashStatus= STL_NOT_TESTED;
}
else
{
Thread_FailSafe_Handler(TMF_ERR_CODE);
}
}

#ifdef ARTI_FAILING_FLASH_TM
STL_SCH_StopArtifFailing();
#endif /* ARTI_FAILING_FLASH_TM */


/**********/
/* RAM TM */
/**********/

#ifdef ARTI_FAILING_RAM_TM
/* forced STL_FAILED status simulates the TM configuration error and
STL_NOT_TESTED status simulates TM run error */
ArtifFailing.RamTmStatus = STL_NOT_TESTED;
STL_SCH_StartArtifFailing(&ArtifFailing);
#endif /* ARTI_FAILING_RAM_TM */


if (StlRamStatus == STL_NOT_TESTED)
{
/* Initialize & configure Flash TM */
if (STL_SCH_InitRam(&StlRamStatus) != STL_OK)
{
Thread_FailSafe_Handler(TMR_ERR_CODE + DEF_PROG_OFFSET);
}
if (STL_SCH_ConfigureRam(&StlRamStatus, &MyRamConfigRunTime) != STL_OK)
{
Thread_FailSafe_Handler(TMR_ERR_CODE + DEF_PROG_OFFSET);
}
else if (StlRamStatus != STL_NOT_TESTED)
{
Thread_FailSafe_Handler(TMR_ERR_CODE);
}
}
/* run partial atomic test */
if (STL_SCH_RunRamTM(&StlRamStatus) != STL_OK)
{
Thread_FailSafe_Handler(TMR_ERR_CODE + DEF_PROG_OFFSET);
}
if (StlRamStatus != STL_PARTIAL_PASSED)
{
if (StlRamStatus == STL_PASSED)
{
/* test completed successfully - to be restarted */
StlRamStatus= STL_NOT_TESTED;
}
else
{
Thread_FailSafe_Handler(TMR_ERR_CODE);
}
}

#ifdef ARTI_FAILING_RAM_TM
STL_SCH_StopArtifFailing();
#endif /* ARTI_FAILING_RAM_TM */

/************/
/* Clock TM */
/************/
/* When artificial failing is activated for clock module, the failure status is
forced by the API module itself available as open source code */
StlClkStatus = STL_NOT_TESTED;
if (STL_RunClockTest(&StlClkStatus) != STL_OK)
{
Thread_FailSafe_Handler(CLK_ERR_CODE + DEF_PROG_OFFSET);
}
if (StlClkStatus != STL_PASSED)
{
Thread_FailSafe_Handler(CLK_ERR_CODE);
}
}

I am developing a firmware using the STM32H523VET6 MCU. I downloaded and am applying the sample below, but the `StlFlashStatus` value returned by the `STL_SCH_RunFlashTM(&StlFlashStatus)` function inside the `STL_RunFullTest()` function is `STL_FAILED`. I would like to know the reason for this. I understand that this example file supports all STM32H5 models; is that not the case?​

Post-Build Steps Command: echo "\"STM32_Programmer_CLI.exe\" -sl \"%CD%\${BuildArtifactFileName}\" 0x08000000 0x08080000 0x400" > call_prg.bat && cmd /c call_prg.bat && arm-none-eabi-objcopy -O binary --gap-fill 0xFF "${BuildArtifactFileBaseName}.elf" "${BuildArtifactFileBaseName}.bin"

Sample : stm32cubeexpansion-x-cube-classb-h5-v4.0.0.zip

Best answer by SimoneST

Hello bupark007,

Thank you for your reply.

When the first CRC calculated by the STL (the value in R0) is incorrect, this is typically due to a mismatch in the library configuration.

To ensure that everything is set up correctly for your device, could you please confirm whether the configuration macros in the stl_user_param_template.c file have been properly set?

You should have something similar to the following in this file:

#define STL_ROM_NO_TZ_START_ADDR (0x08000000UL) /* customizable */ /* No TZ */
#if defined (STM32H563xx)
#define STL_ROM_TZ_S_START_ADDR (0x0C000000UL) /* customizable */ /* TZ, Secure Flash */
#define STL_ROM_TZ_NS_START_ADDR (0x08100000UL) /* customizable */ /* TZ, Non-Secure Flash */
#define STL_ROM_NO_TZ_END_ADDR (0x081FFFFFUL) /* customizable */ /* no TZ: 2 Mbytes */
#define STL_ROM_TZ_S_END_ADDR (0x0C0FFFFFUL) /* customizable */ /* TZ, Secure: 1 Mbytes */
#define STL_ROM_TZ_NS_END_ADDR (0x081FFFFFUL) /* customizable */ /* TZ, Non-Secure: 1 Mbytes */
/* For all devices not supporting TrustZone, addresses related to TrustZone should be initialized to NULL value */
#elif defined (STM32H503xx)
#define STL_ROM_NO_TZ_END_ADDR (0x0801FFFFUL) /* customizable */ /* no TZ: 128 Kbytes */
#define STL_ROM_TZ_S_END_ADDR NULL
#define STL_ROM_TZ_NS_END_ADDR NULL
#define STL_ROM_TZ_S_START_ADDR NULL
#define STL_ROM_TZ_NS_START_ADDR NULL
#else
#define STL_ROM_NO_TZ_END_ADDR (0x0807FFFFUL) /* correct value for STM32H523VET6 */
#define STL_ROM_TZ_S_END_ADDR NULL
#define STL_ROM_TZ_NS_END_ADDR NULL
#define STL_ROM_TZ_S_START_ADDR NULL
#define STL_ROM_TZ_NS_START_ADDR NULL
#endif

Compared to the example, I added:

#define STL_ROM_NO_TZ_END_ADDR   (0x0807FFFFUL) /* correct value for STM32H523VET6 */

Then, for GCC, you should adapt the linker file as shown in the example below: (as in STM32H563ZITX_FLASH.ld)

.data :
{
. = ALIGN(4);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
*(.RamFunc) /* .RamFunc sections */
*(.RamFunc*) /* .RamFunc* sections */

. = ALIGN(4);
_edata = .; /* define a global symbol at data end */
_edata_load = LOADADDR(.data) + SIZEOF(.data) - 4; /* address of the last word of this section, which is also the last word in Flash */
} >RAM AT> FLASH

As described above, _edata_load defines the address of the last word in that section. You should add a similar definition to the last Flash section in your linker file in order to get the last address of your ROM code.

Of course, this piece of code should remain in your file, most likely in Stl_scheduler.c:

/* For GCC (STM32CubeIDE): */
#if (defined (__GNUC__) && !defined (__CC_ARM) && !defined (__ARMCC_VERSION) )
/* symbol _edata_load is defined in linker-script. Its address is the last word of the used part of flash memory. */
extern void *_edata_load;
#define TEST_ROM_END_ADDR ( (uint32_t)&_edata_load + 3)
#endif /* __GNUC__ */

Could you please verify whether these conditions are satisfied?

Thanks,
Best regards

8 replies

ST Employee
June 30, 2026

Hello bupark007,

Yes, the library is compatible with the STM32H523VET6 MCU.
The STM32H523VET6 features 512 KB of Flash, and I can confirm that your post-build command used to generate the CRC is correctly configured.

Reviewing the code, the following macro is correct:

#define TEST_ROM_START_ADDR 0x08000000U

However, the value of:

#define TEST_ROM_END_ADDR 0x0801A3FFU

is not correct. It should be:

#define TEST_ROM_END_ADDR 0x0807FFFFU

This means that the integrity check will cover the entire Flash bank.
Depending on your compiler, make sure that line 38 is not being built in this case.

Please let us know if this answer resolves your issue. Otherwise, we will investigate further. 

Thanks,
Best regards

bupark007Author
Associate
July 1, 2026

Thank you very much for your reply.

However, it is not resolved.

#define TEST_ROM_END_ADDR 0x0807FFFFU

I tried modifying the value as well before asking. It still returns "STL_FAILED".

Line 38 of the code is commented out.

How can I quickly find the cause? 

int main(void)
{

/* USER CODE BEGIN 1 */
/* USER CODE END 1 */

/* MCU Configuration--------------------------------------------------------*/

/* Reset of all peripherals, Initializes the Flash interface and the Systick. */
HAL_Init();

/* USER CODE BEGIN Init */

/* USER CODE END Init */

/* Configure the system clock */
SystemClock_Config();

/* USER CODE BEGIN SysInit */
/* USER CODE END SysInit */

/* Initialize all configured peripherals */
MX_GPIO_Init();
MX_GPDMA1_Init();
MX_ADC1_Init();
MX_FDCAN1_Init();
MX_FDCAN2_Init();
MX_USART1_UART_Init();
MX_SPI3_Init();
MX_SPI2_Init();
MX_I2C1_Init();
MX_DAC1_Init();
MX_CRC_Init();
MX_ICACHE_Init();
MX_RTC_Init();
MX_RAMCFG_Init();
MX_IWDG_Init();
MX_TIM1_Init();
MX_USART2_UART_Init();
MX_TIM15_Init();
/* USER CODE BEGIN 2 */
//FDCAN1 = BOLT EV CAN Communication
//UART1 = ST-LINK
//SPI2 = EEPROM 32Mbit (4MByte)
//SPI3 = Master MCU SPI Communication

// Initialize STL_scheduler, clock test and set up flash & RAM global config structures:
STL_Init();

// Before main application loop, run tests of CPU, entire flash area, entire RAM area, and clock:
STL_RunFullTest();

// MAX6369_WDI_Start();

// System_Init();

/* USER CODE END 2 */

// MX_ThreadX_Init();

/* We should never get here as control is now taken by the scheduler */

/* Infinite loop */
/* USER CODE BEGIN WHILE */
while (1)
{
/* USER CODE END WHILE */

/* USER CODE BEGIN 3 */
}
/* USER CODE END 3 */
}

 

 

ST Employee
July 1, 2026

Hello bupark007,

Unfortunately, it is not easy to debug the library because it is delivered without symbols or source code, for policy reasons.

However, I can suggest the following steps to help identify the issue:

First, I would recommend checking that the device has been properly flashed with the CRC table calculated through STM32CubeProgrammer using the post-build command.

After flashing the device, you should find the CRC table at the address calculated with the following formula:

p_crc = (uint32_t *)(TEST_ROM_END_ADDR - (4UL * (TEST_ROM_END_ADDR + 1UL - TEST_ROM_START_ADDR) / FLASH_SECTION_SIZE) + 1UL)

where FLASH_SECTION_SIZE = 1024.

Since the MCU Flash size is 512 KB, using:

TEST_ROM_START_ADDR = 0x08000000
TEST_ROM_END_ADDR = 0x0807FFFF

the value of p_crc would be:

0x0807F800

So, if you go to this address, you should find something similar to the screenshot below.
 

dump memory at address 0x807f800

If the CRC table is present, we can then compare the CRC calculated by the STL with the CRC stored in Flash.

To do this, set a breakpoint in the VerifySection function, after the call to STL_UTIL_CRC_Calculate, you will find the following instruction:

CMP R6, R0

like:

 

Here, R6 represents the CRC calculated by STM32CubeProgrammer, while R0 represents the CRC calculated by the STL.

 

Please note that, in some cases, STM32CubeProgrammer may fail to correctly calculate the last CRC value when the image size is not a multiple of 1024 bytes. For this reason, in the example, TEST_ROM_END_ADDR is defined as follows:

#pragma section = "FIXED_ORDER_ROM"
#define TEST_ROM_END_ADDR ((uint32_t)TEST_ROM_START_ADDR + __section_size("FIXED_ORDER_ROM") - 1)
#endif /* __ICCARM__ */

This allows the CRC calculation to be limited to the actual size of the image.

We also recommend keeping the EMPTY_PADDING block in the linker file to ensure that the image size ends with a 4-byte alignment.

Could you please verify the steps mentioned above and let us also know which compiler you are using?

Thanks
Best Regards

bupark007Author
Associate
July 2, 2026

Hello, thank you for the quick reply.

I am using the "STM32CubeIDE" tool and the "arm-none-eabi-gcc" compiler.

I verified the CRC value at address 0x0807F800 as shown in the figure below.

 

Additionally, after calling STL_UTIL_CRC_Calculate in the verifySection function and setting the endpoint, I confirmed that the R6 and R0 values ​​are different.

How can I verify why the R0 value is different?

R6 comes out as 0x6d398948, matching the address value of 0x0807F800, while R0 comes out as 0x9ee8b7cd.

Also, I understand that the example source code section you mentioned below is automatically selected and applied depending on the compiler.

/* Toolchain-dependent definitions of TEST_ROM_END_ADDR to define the end of the program in flash */
/* For Keil MDK: */
#if defined (__CC_ARM) || (defined (__ARMCC_VERSION) && (__ARMCC_VERSION >= 6010050))
/* Load$$LR$$LR_IROM1$$Limit is linker-defined symbol following the last byte of the used part of flash memory. */
extern void *Load$$LR$$LR_IROM1$$Limit;
#define TEST_ROM_END_ADDR ( (uint32_t) &Load$$LR$$LR_IROM1$$Limit - 1)
#endif /* _CC_ARM */

/* For GCC (STM32CubeIDE): */
#if (defined (__GNUC__) && !defined (__CC_ARM) && !defined (__ARMCC_VERSION) )
/* symbol _edata_load is defined in linker-script. Its address is the last word of the used part of flash memory. */
extern void *_edata_load;
#define TEST_ROM_END_ADDR ( (uint32_t)&_edata_load + 3)
#endif /* __GNUC__ */

/* For IAR EWARM: */
#if defined (__ICCARM__)
/* All flash content has been placed in FIXED_ORDER_ROM. Its size is added to flash base address to calculate the address of its end.
In linker-script, block FIXED_ORDER_ROM has been defined to contain entire content of flash.
It is placed at the ROM origin and its length is used to calculate the address of its last byte. */
#pragma section = "FIXED_ORDER_ROM"
#define TEST_ROM_END_ADDR ( (uint32_t) TEST_ROM_START_ADDR + __section_size("FIXED_ORDER_ROM") -1)
#endif /* __ICCARM__ */

I understand that FIXED_ORDER_ROM is applied when using the IAR compiler; how can I apply it in GCC?

Thanks
Best Regards

SimoneSTBest answer
ST Employee
July 2, 2026

Hello bupark007,

Thank you for your reply.

When the first CRC calculated by the STL (the value in R0) is incorrect, this is typically due to a mismatch in the library configuration.

To ensure that everything is set up correctly for your device, could you please confirm whether the configuration macros in the stl_user_param_template.c file have been properly set?

You should have something similar to the following in this file:

#define STL_ROM_NO_TZ_START_ADDR (0x08000000UL) /* customizable */ /* No TZ */
#if defined (STM32H563xx)
#define STL_ROM_TZ_S_START_ADDR (0x0C000000UL) /* customizable */ /* TZ, Secure Flash */
#define STL_ROM_TZ_NS_START_ADDR (0x08100000UL) /* customizable */ /* TZ, Non-Secure Flash */
#define STL_ROM_NO_TZ_END_ADDR (0x081FFFFFUL) /* customizable */ /* no TZ: 2 Mbytes */
#define STL_ROM_TZ_S_END_ADDR (0x0C0FFFFFUL) /* customizable */ /* TZ, Secure: 1 Mbytes */
#define STL_ROM_TZ_NS_END_ADDR (0x081FFFFFUL) /* customizable */ /* TZ, Non-Secure: 1 Mbytes */
/* For all devices not supporting TrustZone, addresses related to TrustZone should be initialized to NULL value */
#elif defined (STM32H503xx)
#define STL_ROM_NO_TZ_END_ADDR (0x0801FFFFUL) /* customizable */ /* no TZ: 128 Kbytes */
#define STL_ROM_TZ_S_END_ADDR NULL
#define STL_ROM_TZ_NS_END_ADDR NULL
#define STL_ROM_TZ_S_START_ADDR NULL
#define STL_ROM_TZ_NS_START_ADDR NULL
#else
#define STL_ROM_NO_TZ_END_ADDR (0x0807FFFFUL) /* correct value for STM32H523VET6 */
#define STL_ROM_TZ_S_END_ADDR NULL
#define STL_ROM_TZ_NS_END_ADDR NULL
#define STL_ROM_TZ_S_START_ADDR NULL
#define STL_ROM_TZ_NS_START_ADDR NULL
#endif

Compared to the example, I added:

#define STL_ROM_NO_TZ_END_ADDR   (0x0807FFFFUL) /* correct value for STM32H523VET6 */

Then, for GCC, you should adapt the linker file as shown in the example below: (as in STM32H563ZITX_FLASH.ld)

.data :
{
. = ALIGN(4);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
*(.RamFunc) /* .RamFunc sections */
*(.RamFunc*) /* .RamFunc* sections */

. = ALIGN(4);
_edata = .; /* define a global symbol at data end */
_edata_load = LOADADDR(.data) + SIZEOF(.data) - 4; /* address of the last word of this section, which is also the last word in Flash */
} >RAM AT> FLASH

As described above, _edata_load defines the address of the last word in that section. You should add a similar definition to the last Flash section in your linker file in order to get the last address of your ROM code.

Of course, this piece of code should remain in your file, most likely in Stl_scheduler.c:

/* For GCC (STM32CubeIDE): */
#if (defined (__GNUC__) && !defined (__CC_ARM) && !defined (__ARMCC_VERSION) )
/* symbol _edata_load is defined in linker-script. Its address is the last word of the used part of flash memory. */
extern void *_edata_load;
#define TEST_ROM_END_ADDR ( (uint32_t)&_edata_load + 3)
#endif /* __GNUC__ */

Could you please verify whether these conditions are satisfied?

Thanks,
Best regards

bupark007Author
Associate
July 3, 2026

 

Hello SimoneST

Thank you for your reply. I have already checked and tested the points mentioned above.

I have found the cause and would like to share it with you.

It turned out to be an issue with the build and download methods, not the source code.

When I proceeded to download the file while debugging in STM32CubeIDE after building the source code, the CRC error was STL_FAILED.

In STM32CubeIDE, I only performed Ctrl+B (Build) to create the *.bin file.

Then, I proceeded to download the *.bin file using STM32CubeProgrammer.

I confirmed that it is working without any issues.

Note: I have also modified the Post-Build Steps.

Existing: echo "\"STM32_Programmer_CLI.exe\" -sl \"%CD%\${BuildArtifactFileName}\" 0x08000000 0x08080000 0x400" > call_prg.bat && cmd /c call_prg.bat && arm-none-eabi-objcopy -O binary --gap-fill 0xFF "${BuildArtifactFileBaseName}.elf" "${BuildArtifactFileBaseName}.bin"

Change:arm-none-eabi-objcopy -O binary --gap-fill 0xFF "${BuildArtifactFileBaseName}.elf" "${BuildArtifactFileBaseName}.bin" && echo "\"STM32_Programmer_CLI.exe\" -sl \"%CD%\\${BuildArtifactFileBaseName}.bin\" 0x08000000 0x08080000 0x400" > call_prg.bat && cmd /c call_prg.bat

That is all. Thank you for your response.

However, I could not find a solution for the STL_FAILED issue that occurs when downloading during Debug in STM32CubeIDE.

ST Employee
July 3, 2026

Hello bupark007,

Thank you very much for your feedback. I am glad to hear that you have found the root cause and that the issue is now almost resolved.

Regarding the new command you are using:

arm-none-eabi-objcopy -O binary --gap-fill 0xFF "${BuildArtifactFileBaseName}.elf" "${BuildArtifactFileBaseName}.bin" && echo "\"STM32_Programmer_CLI.exe\" -sl \"%CD%\\${BuildArtifactFileBaseName}.bin\" 0x08000000 0x08080000 0x400" > call_prg.bat && cmd /c call_prg.bat

I noticed that STM32CubeProgrammer is being called with the .bin file as input. Typically, during debug, STM32CubeIDE flashes the .elf file instead, so that symbols are available for debugging.

Therefore, to make it work during debug, you could try using the same command but replacing the .bin file with the .elf file:

arm-none-eabi-objcopy -O binary --gap-fill 0xFF "${BuildArtifactFileBaseName}.elf" "${BuildArtifactFileBaseName}.bin" && echo "\"STM32_Programmer_CLI.exe\" -sl \"%CD%\\${BuildArtifactFileBaseName}.elf\" 0x08000000 0x08080000 0x400" > call_prg.bat && cmd /c call_prg.bat

In other words, using ${BuildArtifactFileBaseName}.elf instead of ${BuildArtifactFileBaseName}.bin  when calling the STM32_Programmer_CLI.exe. 
We should make sure that the CRC table is added to the same file that we are currently running/debugging.

Could you please confirm whether this works on your side?

Thanks,
Best regards!

bupark007Author
Associate
July 6, 2026

Hello SimoneST,

 

I modified the .bin file to .elf as you suggested and tested it, but the same STL_FAILED error occurs in the STL_RunFullTest() function.

The CRC data has been added to memory.

However, when I checked the R6 and R0 values ​​after the verifySection function, the R0 value came out differently.

 

Thanks