2025-04-22 1:45 PM - last edited on 2025-04-23 6:06 AM by Amel NASRI
I’m using the Class B Flash self‑test on an STM32F407. My post‑build step injects CRCs with:
STM32_Programmer_CLI -sl Firmware.elf 0x08000000 0x08100000 0x400
I Inspected the CRC of the first section via
arm-none-eabi-objdump -s -j "CRCs area" Firmware.elf | head -n2
After this I re‑computed the CRC to verify which method it used. I used srec_cat and I used the following method:
arm-none-eabi-objcopy -O binary Firmware.elf full_flash.bin
dd if=full_flash.bin of=region.bin bs=1 count=1024
srec_cat region.bin -Binary \
-CRC32_Little_Endian 0x400 \
-crop 0x400 0x404 \
-output - -binary \
| hexdump -e '1/4 "CRC32: 0x%08X\n"'
After this I figured out that the CRC checksum stored in the memory for the first 0x400 section matched with the srec_cat output.
However at runtime, when STL_SCH_RunFlashTM() (compiled with both -DSTL_SW_CRC and without it) computes the CRC over the exact same 0x400 bytes which is different than the expected value. As a result, StlFlashStatus returns STL_Failed I end up in the FailSafe_Handler with error code 13.
Key finding:
The built‑in STM32F4 CRC peripheral has no bit reflections, initial bit is 0xFFFFFFFF, no final XOR. Whereas -CRC32_Little_endian (same as STM_Programmer_CLI) uses reflected input and output, consistent with the standard CRC-32 definition. A final XOR of 0xFFFFFFFF is applied always injects the reflected CRC32 (input+output bit‑reversal), so the two algorithms cannot match on F4 hardware.
The whole method of CRC calculation seems unusual to me. Maybe it's my lack of knowledge too. Any guidance or best‑practice example for aligning the runtime CRC with the CLI‑injected table on STM32F4 would be greatly appreciated.
2025-04-22 2:17 PM
srecord does have the STM32-specific CRC implemented, see it's manual.
JW
2025-04-22 3:11 PM - edited 2025-04-22 3:11 PM
>>The whole method of CRC calculation seems unusual to me.
The HW is super awkward, it does 32-bit word level processing, reads the words small-endian and then processes left-ward with the high order bit first.
Most online calculators will choke if fed bytes in memory order.
https://github.com/cturvey/RandomNinjaChef/blob/main/stm32crc.c
2025-04-23 5:25 AM
Hey @waclawek.jan , yes I've seen that too and tried it. It results in a different checksum from what is stored in the CRCs area of the memory. Which leads me to believe that STM_Programmer_CLI uses -CRC32_Little_Endian configurations (which is different than STM32F407's hardware CRC engine).
2025-04-23 5:37 AM
In X-Cube-ClassB-F4, all the safety functions including the checksum calculation function is under STL_Lib.a library. According to the documentation, all I know is that the flash test STL_SCH_RunFlashTM() calls the CRC calculation function CRC_Handle_32_HW(). I don't have the visibility of what's inside. It likely calls the HAL function to calculate the CRC but I don't know.
The issue is it's a pre-certified library and I shouldn't modify any core logic. That's why wanted to figure out if anyone in the community faced similar issues or not.
Maybe an ST employee can answer this? @Petr Sladecek @Amel NASRI
2025-05-05 7:30 AM
Hello all,
STL use no HAL functions and simply apply default setting of the embedded CPU HW CRC unit to calculate the checking patterns. The CRC result descriptor field is made by CLI of the STM32 Cube Programmer at the end of the flash area. To make the CRC calculations working correctly, user must ensure required alignments of the binary area start and end and the area must be continuous (any gaps between sections could make a trouble due to their different default filling by different programmers). Anyway, customer can develop and certify own method to check the program memory integrity at his responsibility.
Best regards,
Petr
2025-05-06 6:14 AM
Thanks for your reply Petr,
But can you further explain to me how do I verify the start and the end area of the binary is continuous? This is my linker file FYI:
/*
******************************************************************************
**
** @file : LinkerScript.ld
**
** @author : Auto-generated by STM32CubeIDE
**
** @brief : Linker script for STM32F407ZGTx Device from STM32F4 series
** 1024KBytes FLASH
** 64KBytes CCMRAM
** 128KBytes RAM
**
** Set heap size, stack size and stack location according
** to application requirements.
**
** Set memory bank area and size if external memory is used
**
** Target : STMicroelectronics STM32
**
** Distribution: The file is distributed as is, without any warranty
** of any kind.
**
******************************************************************************
** @attention
**
** Copyright (c) 2025 STMicroelectronics.
** All rights reserved.
**
** This software is licensed under terms that can be found in the LICENSE file
** in the root directory of this software component.
** If no LICENSE file comes with this software, it is provided AS-IS.
**
******************************************************************************
*/
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = ORIGIN(RAM) + LENGTH(RAM); /* end of "RAM" Ram type memory */
_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */
/* Memories definition */
MEMORY
{
CCMRAM (xrw) : ORIGIN = 0x10000000, LENGTH = 64K
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 128K
FLASH (rx) : ORIGIN = 0x8000000, LENGTH = 1024K
}
/* Sections */
SECTIONS
{
/* The startup code into "FLASH" Rom type memory */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >FLASH
/* The program code and other data into "FLASH" Rom type memory */
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
_etext = .; /* define a global symbols at end of code */
} >FLASH
/* Constant data into "FLASH" Rom type memory */
.rodata :
{
. = ALIGN(4);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
} >FLASH
.ARM.extab (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
{
. = ALIGN(4);
*(.ARM.extab* .gnu.linkonce.armextab.*)
. = ALIGN(4);
} >FLASH
.ARM (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
{
. = ALIGN(4);
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
. = ALIGN(4);
} >FLASH
.preinit_array (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
{
. = ALIGN(4);
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
. = ALIGN(4);
} >FLASH
.init_array (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
{
. = ALIGN(4);
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
. = ALIGN(4);
} >FLASH
.fini_array (READONLY) : /* The "READONLY" keyword is only supported in GCC11 and later, remove it if using GCC10 or earlier. */
{
. = ALIGN(4);
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
. = ALIGN(4);
} >FLASH
/* reserved space at the beginning of RAM to be used by STL RAM test */
backup_buffer_section (NOLOAD): { *(backup_buffer_section) } >RAM
/* Used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections into "RAM" Ram type memory */
.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; /* Define address of the last word of this section, which is also the last word in the flash. */
} >RAM AT> FLASH
_siccmram = LOADADDR(.ccmram);
/* CCM-RAM section
*
* IMPORTANT NOTE!
* If initialized variables will be placed in this section,
* the startup code needs to be modified to copy the init-values.
*/
.ccmram :
{
. = ALIGN(4);
_sccmram = .; /* create a global symbol at ccmram start */
*(.ccmram)
*(.ccmram*)
. = ALIGN(4);
_eccmram = .; /* create a global symbol at ccmram end */
} >CCMRAM AT> FLASH
/* Uninitialized data section into "RAM" Ram type memory */
. = ALIGN(4);
.bss :
{
/* This is used by the startup in order to initialize the .bss section */
_sbss = .; /* define a global symbol at bss start */
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = _ebss;
} >RAM
/* User_heap_stack section, used to check that there is enough "RAM" Ram type memory left */
._user_heap_stack :
{
. = ALIGN(8);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(8);
} >RAM
/* Remove information from the compiler libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
.ARM.attributes 0 : { *(.ARM.attributes) }
}