Skip to main content
Associate
June 19, 2026
Solved

flash/ECC issue on an STM32H743.

  • June 19, 2026
  • 8 replies
  • 105 views

I'm investigating a suspected flash corruption/ECC issue on an STM32H743.

A read from flash address 0x081F0000 consistently causes a fault. The problem is that I'm not fully confident that the fault status registers can be trusted after the event, so I'm looking for a diagnostic approach that does not rely entirely on the captured fault information.

Known facts:

  • Accessing 0x081F0000 triggers a fault.
  • ECC_FA2R does not show a failing address.
  • STM32H7 flash ECC is evaluated only when flash is read.
  • ECC granularity is 32 bytes (256 bits).

My main question is:

If I suspect flash corruption or an ECC problem, but do not fully trust the contents of CFSR, HFSR, BFAR, SR2, or ECC_FA2R after the fault, what is the recommended way to diagnose the problem?

Would you:

  1. Perform a controlled BusFault-protected flash scan in 32-byte steps?
  2. Use HAL_FLASHEx_ComputeCRC() over the suspect region?
  3. Dump the entire flash bank via SWD/JTAG and verify externally?
  4. Use some other method?

Has anyone seen a corrupted flash word on STM32H7 that caused faults without providing useful ECC fault information?

I'm interested in the most reliable way to determine whether the root cause is:

  • Double-bit ECC corruption,
  • Flash content corruption,
  • MPU/PCROP/protection settings,
  • Or something else entirely.
Best answer by Pieter Egg

> work around on ECC bit not becoming active : DBECCERR)

Aha, so you suspect  ECC bit not becoming active, due to two-bit errors?
→ indeed :-)

> To return from the busfault handler, we needed some assembly code to have a recovery pointer.

This and filling the info in the g_fault_... variables is not included in the shown code. Is it already written? Does it run and reveal something about the reason of the fault?
→ the main reason / cause of the issue, the bits not becoming active we actually did not found / could not verify.
→ we call this a work around because in the bus fault handler we can read g_fault_sr2 and g_fault_ecc2 

#include "flash_probe.h"

void BusFault_Handler_C(uint32_t *stack);

__asm void BusFault_Handler(void)
{
IMPORT  BusFault_Handler_C

    TST     LR, #4
    ITE     EQ
    MRSEQ   R0, MSP
    MRSNE   R0, PSP
    B       BusFault_Handler_C
}

void BusFault_Handler_C(uint32_t *stack)
{
    if (g_flash_probe_active)
    {
        g_fault_cfsr = SCB->CFSR;
        g_fault_hfsr = SCB->HFSR;

        if (SCB->CFSR & SCB_CFSR_BFARVALID_Msk)
        {
            g_fault_bfar = SCB->BFAR;
        }

        g_fault_sr2  = FLASH->SR2;
        g_fault_ecc2 = FLASH->ECC_FA2;

        g_flash_probe_active = 0;
        g_flash_probe_failed = 1;

        /*
           Clear BusFault bits.
           CFSR bits are write-1-to-clear.
        */
        SCB->CFSR = SCB_CFSR_IBUSERR_Msk     |
                    SCB_CFSR_PRECISERR_Msk   |
                    SCB_CFSR_IMPRECISERR_Msk |
                    SCB_CFSR_UNSTKERR_Msk    |
                    SCB_CFSR_STKERR_Msk      |
                    SCB_CFSR_LSPERR_Msk      |
                    SCB_CFSR_BFARVALID_Msk;

//#ifdef FLASH_CCR_CLR_DBECCERR
        FLASH->CCR2 = FLASH_CCR_CLR_DBECCERR |
                      FLASH_CCR_CLR_SNECCERR;
//#endif

        /*
           stack[6] is the stacked PC.
           Redirect execution to the assembly recovery label.
        */
        stack[6] = g_flash_probe_recovery_pc;

        __DSB();
        __ISB();

        return;
    }

    while (1)
    {
        /*
           Unexpected BusFault.
           Do not try to recover.
        */
    }
}

The “corrupt code” check.. where we can check if the sector faults are properly set (because now we can trust on them)

#include "flash_probe.h"

void flash_bank2_corrupt_check(uint32_t a_start_addr, uint32_t a_size)
{

// Clear old Bank 2 ECC / CRC-related flags first
FLASH->CCR2 = FLASH_CCR_CLR_EOP
| FLASH_CCR_CLR_WRPERR
| FLASH_CCR_CLR_PGSERR
| FLASH_CCR_CLR_STRBERR
| FLASH_CCR_CLR_INCERR
| FLASH_CCR_CLR_OPERR
| FLASH_CCR_CLR_RDPERR
| FLASH_CCR_CLR_RDSERR
| FLASH_CCR_CLR_SNECCERR
| FLASH_CCR_CLR_DBECCERR
| FLASH_CCR_CLR_CRCEND
| FLASH_CCR_CLR_CRCRDERR;

//Enable BusFault:
SCB->SHCSR |= SCB_SHCSR_BUSFAULTENA_Msk;

//Read the entire sector in 32-byte increments:
g_fault_sr2 = 0; //
g_fault_ecc2 = 0;
g_fault_cfsr = 0;
g_fault_hfsr = 0;
g_fault_bfar = 0;

uint32_t sector_end = a_start_addr+a_size;//0x08200000;
for (uint32_t addr = a_start_addr; addr < sector_end; addr += 32) //The test need to be done only every 32 bytes
{
uint32_t dummy;

if (!Flash_TryRead32(addr, &dummy))
{
// addr is inside a failing 32-byte ECC flash word
if ((g_fault_sr2 & (/*FLASH_FLAG_SNECCERR_BANK2 |*/ FLASH_FLAG_DBECCERR_BANK2)) && g_fault_ecc2)
{
uint8_t i = GetSector(FLASH_BANK2_BASE + (g_fault_ecc2*32)); //eg. 0x00006000 => 0x08100000 + (0x00006000 *32) => 0x081c0000
if (i>=FLASH_SECTOR_TOTAL)
{ //unvalid sector nr
#ifdef __TARGET_DEBUG__
/* Infinite loop */
Error_Handler();//while (1);
#else
;
#endif
}
else
{
sys_flash_ECC_failures_sector_bits_on_bank[1] |= (1<<i);
}
}
break;
}
}

}
#endif


 

You haven’t answered about possibility of writing same flash location twice.
→ this is not relevant for us :-) 

I hope this post makes it more clear of how the issue is tackled.

8 replies

Pavel A.
June 19, 2026

Have you tried to erase the sector 15 and test reading before programming? How do you put code or data into this sector: by a programmer tool or by in-app programming? If the latter, is the “parallelism” value good? Writing to the same address several times?

> Perform a controlled BusFault-protected flash scan in 32-byte steps?

Yes

> Use HAL_FLASHEx_ComputeCRC() over the suspect region?

No. IIRC there’s errata against using this method.

 

AScha.3
Super User
June 20, 2026

>seen a corrupted flash word on STM32H7

No , not me.

+

Try with CubeProgrammer : writing the flash, then read the ..81F… area. So you know: chip sick - or your way to flash it.

If you feel a post has answered your question, please click on " Best Answer ".
TDK
June 22, 2026

You say you don’t trust the chip but you don’t say what it is telling you. What is the content of the hard fault SCB registers? Chip can generally be trusted. That would be the straightforward debug approach, rather than jumping through hoops to avoid a problem that may not even exist.

If reading 0x081F0000 triggers an ECC fault, then that address was likely written to twice. Only you know what your program does and how that could have been achieved.

"If you feel a post has answered your question, please click ""Accept as Solution""."
Associate
June 22, 2026

Hi,

Thanks for the reply’s :-)

 

At the moment, a proof of concept is ready to test and it’s like this (work around on ECC bit not becoming active : DBECCERR)

 

internal flash read with bus fault enabled 
if corrupted flash → bus fault handler is triggered
withing the busfault handler, the ECC error BIT & Address is used to know it was the memory read that was active.
To return from the busfault handler, we needed some assembly code to have a recovery pointer.

 

During boot, following block is executed during boot where the busfault is enabled

    //Enable BusFault:
    SCB->SHCSR |= SCB_SHCSR_BUSFAULTENA_Msk;    


full function
 

#include "flash_probe.h"

void flash_bank2_corrupt_check(uint32_t a_start_addr, uint32_t a_size)
{

    // Clear old Bank 2 ECC / CRC-related flags first
    FLASH->CCR2 = FLASH_CCR_CLR_EOP
                | FLASH_CCR_CLR_WRPERR
                | FLASH_CCR_CLR_PGSERR
                | FLASH_CCR_CLR_STRBERR
                | FLASH_CCR_CLR_INCERR
                | FLASH_CCR_CLR_OPERR
                | FLASH_CCR_CLR_RDPERR
                | FLASH_CCR_CLR_RDSERR
                | FLASH_CCR_CLR_SNECCERR
                | FLASH_CCR_CLR_DBECCERR
                | FLASH_CCR_CLR_CRCEND
                | FLASH_CCR_CLR_CRCRDERR;
    
    //Enable BusFault:
    SCB->SHCSR |= SCB_SHCSR_BUSFAULTENA_Msk;    
    
    //Read the entire sector in 32-byte increments:

    g_fault_sr2 = 0; 
    g_fault_ecc2 = 0;
    g_fault_cfsr = 0;
    g_fault_hfsr = 0;
    g_fault_bfar = 0;
    g_fault_sr2 = 0;
    g_fault_ecc2 = 0;
    
    uint32_t sector_end = a_start_addr+a_size;//0x08200000;
    for (uint32_t addr = a_start_addr; addr < sector_end; addr += 32) //The test need to be done only every 32 bytes
    {
        uint32_t dummy;

        if (!Flash_TryRead32(addr, &dummy))
        {
            // addr is inside a failing 32-byte ECC flash word
            if ((g_fault_sr2  & (/*FLASH_FLAG_SNECCERR_BANK2 |*/ FLASH_FLAG_DBECCERR_BANK2)) && g_fault_ecc2) //only double bit ECC (FLASH_FLAG_DBECCERR_BANK2), because single bit ECC (FLASH_FLAG_SNECCERR_BANK2) is autocorrecting
            {
                uint8_t i = GetSector(FLASH_BANK2_BASE + (g_fault_ecc2*32)); //eg. 0x00006000 => 0x08100000 + (0x00006000 *32) => 0x081c0000
                if (i>=FLASH_SECTOR_TOTAL)
                { //unvalid sector nr
                    #ifdef __TARGET_DEBUG__
                    /* Infinite loop */
                    Error_Handler();//while (1);
                    #else
                    ;
                    #endif
                }
                else
                {
                    sys_flash_ECC_failures_sector_bits_on_bank[1] |= (1<<i);    
                }
            }            
            break;
        }
    }
    
}
#endif



If more code should be needed to have a full look on this topic, please ask and I’ll share it :-) 

So far so good :-) 

thanks

Pavel A.
June 22, 2026

> work around on ECC bit not becoming active : DBECCERR)

Aha, so you suspect  ECC bit not becoming active, due to two-bit errors?

> To return from the busfault handler, we needed some assembly code to have a recovery pointer.

This and filling the info in the g_fault_... variables is not included in the shown code. Is it already written? Does it run and reveal something about the reason of the fault?

You haven’t answered about possibility of writing same flash location twice.

 

Pieter EggAuthorBest answer
Associate
June 23, 2026

> work around on ECC bit not becoming active : DBECCERR)

Aha, so you suspect  ECC bit not becoming active, due to two-bit errors?
→ indeed :-)

> To return from the busfault handler, we needed some assembly code to have a recovery pointer.

This and filling the info in the g_fault_... variables is not included in the shown code. Is it already written? Does it run and reveal something about the reason of the fault?
→ the main reason / cause of the issue, the bits not becoming active we actually did not found / could not verify.
→ we call this a work around because in the bus fault handler we can read g_fault_sr2 and g_fault_ecc2 

#include "flash_probe.h"

void BusFault_Handler_C(uint32_t *stack);

__asm void BusFault_Handler(void)
{
IMPORT  BusFault_Handler_C

    TST     LR, #4
    ITE     EQ
    MRSEQ   R0, MSP
    MRSNE   R0, PSP
    B       BusFault_Handler_C
}

void BusFault_Handler_C(uint32_t *stack)
{
    if (g_flash_probe_active)
    {
        g_fault_cfsr = SCB->CFSR;
        g_fault_hfsr = SCB->HFSR;

        if (SCB->CFSR & SCB_CFSR_BFARVALID_Msk)
        {
            g_fault_bfar = SCB->BFAR;
        }

        g_fault_sr2  = FLASH->SR2;
        g_fault_ecc2 = FLASH->ECC_FA2;

        g_flash_probe_active = 0;
        g_flash_probe_failed = 1;

        /*
           Clear BusFault bits.
           CFSR bits are write-1-to-clear.
        */
        SCB->CFSR = SCB_CFSR_IBUSERR_Msk     |
                    SCB_CFSR_PRECISERR_Msk   |
                    SCB_CFSR_IMPRECISERR_Msk |
                    SCB_CFSR_UNSTKERR_Msk    |
                    SCB_CFSR_STKERR_Msk      |
                    SCB_CFSR_LSPERR_Msk      |
                    SCB_CFSR_BFARVALID_Msk;

//#ifdef FLASH_CCR_CLR_DBECCERR
        FLASH->CCR2 = FLASH_CCR_CLR_DBECCERR |
                      FLASH_CCR_CLR_SNECCERR;
//#endif

        /*
           stack[6] is the stacked PC.
           Redirect execution to the assembly recovery label.
        */
        stack[6] = g_flash_probe_recovery_pc;

        __DSB();
        __ISB();

        return;
    }

    while (1)
    {
        /*
           Unexpected BusFault.
           Do not try to recover.
        */
    }
}

The “corrupt code” check.. where we can check if the sector faults are properly set (because now we can trust on them)

#include "flash_probe.h"

void flash_bank2_corrupt_check(uint32_t a_start_addr, uint32_t a_size)
{

// Clear old Bank 2 ECC / CRC-related flags first
FLASH->CCR2 = FLASH_CCR_CLR_EOP
| FLASH_CCR_CLR_WRPERR
| FLASH_CCR_CLR_PGSERR
| FLASH_CCR_CLR_STRBERR
| FLASH_CCR_CLR_INCERR
| FLASH_CCR_CLR_OPERR
| FLASH_CCR_CLR_RDPERR
| FLASH_CCR_CLR_RDSERR
| FLASH_CCR_CLR_SNECCERR
| FLASH_CCR_CLR_DBECCERR
| FLASH_CCR_CLR_CRCEND
| FLASH_CCR_CLR_CRCRDERR;

//Enable BusFault:
SCB->SHCSR |= SCB_SHCSR_BUSFAULTENA_Msk;

//Read the entire sector in 32-byte increments:
g_fault_sr2 = 0; //
g_fault_ecc2 = 0;
g_fault_cfsr = 0;
g_fault_hfsr = 0;
g_fault_bfar = 0;

uint32_t sector_end = a_start_addr+a_size;//0x08200000;
for (uint32_t addr = a_start_addr; addr < sector_end; addr += 32) //The test need to be done only every 32 bytes
{
uint32_t dummy;

if (!Flash_TryRead32(addr, &dummy))
{
// addr is inside a failing 32-byte ECC flash word
if ((g_fault_sr2 & (/*FLASH_FLAG_SNECCERR_BANK2 |*/ FLASH_FLAG_DBECCERR_BANK2)) && g_fault_ecc2)
{
uint8_t i = GetSector(FLASH_BANK2_BASE + (g_fault_ecc2*32)); //eg. 0x00006000 => 0x08100000 + (0x00006000 *32) => 0x081c0000
if (i>=FLASH_SECTOR_TOTAL)
{ //unvalid sector nr
#ifdef __TARGET_DEBUG__
/* Infinite loop */
Error_Handler();//while (1);
#else
;
#endif
}
else
{
sys_flash_ECC_failures_sector_bits_on_bank[1] |= (1<<i);
}
}
break;
}
}

}
#endif


 

You haven’t answered about possibility of writing same flash location twice.
→ this is not relevant for us :-) 

I hope this post makes it more clear of how the issue is tackled.

Pavel A.
June 23, 2026

Thank you for providing the details.

So far, have you determined the root cause of these BusFaults?

 

Associate
June 24, 2026

> So far, have you determined the root cause of these BusFaults?
→ no, we did not found the root cause of the busfaults .. does not feel OK but not sure what else we can do about it at the moment .. :-)