Skip to main content
Associate III
October 23, 2025
Solved

HARDFAULT STM32G0 ThreadX

  • October 23, 2025
  • 4 replies
  • 748 views

I’m working with an STM32G0 running ThreadX (Azure RTOS).
The system UART is configured in circular DMA mode with an IDLE event interrupt for data reception.

Symptoms
Intermittently, after hundreds of successful receptions, the system enters HardFault_Handler.
The fault always occurs at the same point in the code (on a C division instruction, inside a function unrelated to DMA).

The register values at the HardFault are shown in Image 1.

When it runs without triggering a hard fault, the registers are:
xPSR = 0x01000000 and CONTROL = 2.

When the hard fault occurs, these registers are:
xPSR = 0x00000000 and CONTROL = 0.

The failure does not depend on the received data.

The thread stack (PSP) has sufficient space.

I believe the intermittent HardFault is not related to the operation itself:
If I modify the code—adding new instructions or removing code—sometimes it reproduces, and other times it still reproduces but in different parts of the code.

Question
What can I do to determine the root cause of this hard fault? I can’t figure out what the problem is.

Best answer by fernandogamax

It looks like I’ve found the problem: it’s a microcontroller bug related to prefetch. To prevent these random hard faults, we need to disable it.

https://www.st.com/resource/en/errata_sheet/es0547-stm32g0b0cekereve-device-errata-stmicroelectronics.pdf 


4 replies

Technical Moderator
October 24, 2025

Hello @fernandogamax 

Please follow the instruction in the article below to debug your HardFault:

How to debug a HardFault on an Arm® Cortex®-M STM3... - STMicroelectronics Community

"To give better visibility on the answered topics, please click on ""Accept as Solution"" on the reply which solved your issue or answered your question.Saket_Om"
Associate III
October 24, 2025

Hello @fernandogamax
I have followed the steps you indicated. I'm working with an M0+ STM32G0B0, so I cannot access the hardfault analyzer.

r0 0x0
r1 0x0
r2 0x0
r3 0x2000437b
r4 0x0
r5 0x24
r6 0x1c
r7 0x20004360
r8 0x0
r9 0x0
r10 0x0
r11 0x0
r12 0x1
sp 0x20004360
lr 0x8040291
pc 0x80402a0
xpsr 0x0
msp 0x20023fc8
psp 0x20004360
primask 0x0
basepri 0x0
faultmask 0x0
control 0x0

 

centesimas=(int)parte_decimal%((int)multipliccador2/10);
0804029a: adds r3, r7, r6
0804029c: ldrb r4, [r3, #0]
0804029e: adds r3, r7, r5
080402a0: ldrh r3, [r3, #0]


The instruction ldrh r3, [r3, #0] is using an odd address in r3 (0x2000437b). Could this be the cause of the hardfault? But what is causing this in the first place?"

 

Tesla DeLorean
Guru
October 24, 2025

Look at surrounding code. The misaligned 16 bit read is the problem. Is that from a pointer? You need to do a more guarded read where you get individual bytes to construct the word.

Is this code processing/parsing data sent to you?

Don't cast *uint8_t pointers to *uint16_t or *uint32_t ones.

Tips, Buy me a coffee, or three.. PayPal VenmoUp vote any posts that you find helpful, it shows what's working..
Associate III
October 24, 2025
In the function where the hardfault occurs, no pointers are used.
 
 
void G7SL_pinta_float(float dato,uint8_t numero_decimales)
{
int temp, temp1,numero_entero;
uint8_t signo = EN_GS7_SIGNO_POSITIVO;
uint32_t unidad = 0, decena = 0, centena = 0, unidad_millar = 0, decena_millar = 0,parte_decimal = 0,decimas = 0, centesimas = 0;
uint32_t multipliccador1,multipliccador2;
uint32_t digito_pintar=0;
if(dato < -0.01){
signo = EN_GS7_SIGNO_NEGATIVO;
}
switch (numero_decimales)
{
case 0:multipliccador1=10;multipliccador2=1;break;
case 1:multipliccador1=100;multipliccador2=10;break;
case 2:multipliccador1=1000;multipliccador2=100;break;
default:multipliccador1=10000;multipliccador2=1000;break;
}
//Pasamos el float a un valor entero tomando dos decimales y en valor absoluto.
temp = abs((int)(dato *(int)multipliccador1));
//Pamos el valor entero teniendo en cuenta dos decimales a un valor entero con un solo decimal.
temp1 = temp / 10;
//Comprobamos si tenemos que redondear
if((temp-(temp1*10))>5)
{
    temp1 = temp1 + 1;
}
parte_decimal = temp1%(int)multipliccador2;
//parte_decimal = temp1*multipliccador2;
numero_entero = temp1/(int)multipliccador2;
decena_millar = (numero_entero%100000)/10000;
unidad_millar = (numero_entero%10000)/1000;
centena = (numero_entero%1000)/100;
decena = (numero_entero%100)/10;
unidad = (numero_entero%10)/1;
decimas=(int)parte_decimal/((int)multipliccador2/10);
centesimas=(int)parte_decimal%((int)multipliccador2/10);//->hardfault
 
}
 
I've been dealing with this problem for a long time. When I modify the code, the error might not reappear, or it might appear in another area of the code. It's random because, before the hardfault jumps, that piece of code may have executed 1000 times without the hardfault occurring."
Associate III
October 27, 2025


I’ve changed the code so that a division by 0 can never occur, although, as I mentioned earlier, this case never actually happened in my tests.

When I use this code, a hard fault occurs:

----------------------------------------------------------------------------------------------------------

uint32_t decena_millar = (uint32_t)((numero_entero / 10000) % 10);

uint32_t unidad_millar = (uint32_t)((numero_entero / 1000) % 10);

uint32_t centena = (uint32_t)((numero_entero / 100) % 10);

uint32_t decena = (uint32_t)((numero_entero / 10) % 10);

uint32_t unidad = (uint32_t)( numero_entero % 10);

----------------------------------------------------------------------------------------------------------

When I use this other code, no hard fault occurs:

----------------------------------------------------------------------------------------------------------

uint32_t decena_millar = (uint32_t)((numero_entero / 10000));

decena_millar=decena_millar% 10;

uint32_t unidad_millar = (uint32_t)((numero_entero / 1000));

unidad_millar=unidad_millar%10;

uint32_t centena = (uint32_t)((numero_entero / 100));

centena=centena%10;

uint32_t decena = (uint32_t)((numero_entero / 10));

decena=decena%10;

uint32_t unidad = (uint32_t)( numero_entero );

unidad=unidad%10;

-----------------------------------------------------------------------------------------------

It doesn’t make sense—I think the problem hasn’t been solved; it’s just been moved somewhere else.

Another thing I’m thinking about: a few months ago I migrated the project from STM32CubeIDE 1.12.1 to STM32CubeIDE 1.18.0. We’re seeing that before this migration we never encountered this issue. Could that be the problem?

Any ideas?

fernandogamaxAuthorBest answer
Associate III
October 28, 2025

It looks like I’ve found the problem: it’s a microcontroller bug related to prefetch. To prevent these random hard faults, we need to disable it.

https://www.st.com/resource/en/errata_sheet/es0547-stm32g0b0cekereve-device-errata-stmicroelectronics.pdf 


February 9, 2026

This behavior still strongly points to memory corruption rather than the division instruction itself.

The corrupted xPSR = 0x00000000 and CONTROL = 0 indicate an invalid CPU state, which commonly happens when something overwrites RTOS context data, thread stacks, or the ISR stack. The fact that the crash location changes when code is added/removed is a classic sign of stack overflow or DMA buffer overrun, especially with UART circular DMA + IDLE interrupt handling.

To track the root cause:

  • Enable MemManage, BusFault, and UsageFault and inspect CFSR/HFSR/BFAR/MMFAR.

  • Check both PSP and MSP usage; DMA-driven interrupts often overflow the ISR stack.

  • Ensure DMA buffers are not on the stack, are correctly aligned, and never accessed out of bounds.

  • Guard DMA buffers and thread stacks with known patterns or enable ThreadX stack checking.

  • Capture stacked registers (PC/LR/SP) inside HardFault_Handler to confirm which context failed.

Once the first memory overwrite is caught, the HardFault usually becomes obvious.

(Unrelated, but if you also enjoy debugging-intensive systems in a more relaxed way, you can explore Nulls Brawl at https://nulls-brawl.io/ — a sandbox-style game server great for experimenting freely.)