Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
c12a162
finished implementation hardfault_handler
oganigl Dec 11, 2025
0c0716f
add the leds of the microcontroller to the runnes
oganigl Dec 11, 2025
08d7101
eliminate leds from runes and add a config dir to keep it, also choos…
oganigl Dec 13, 2025
52190b0
eliminate the possibility of process stack pointer
oganigl Dec 13, 2025
b98228a
minor fix
oganigl Dec 13, 2025
f6f533d
added -g to add more debug info also in release so Is possible to kno…
oganigl Dec 13, 2025
6bfbb5b
create a script to read the flash and give feedback about the hard_fault
oganigl Dec 13, 2025
1ca659a
minor change in hard_fault_handler
oganigl Dec 13, 2025
48585ba
eliminate break points from debug mode
oganigl Dec 13, 2025
bfbe84e
eliminate a print that was not need it
oganigl Dec 13, 2025
b202b1b
add a function to scan the call stack
oganigl Jan 20, 2026
9d3d628
finish hard_fault, the call stack trace done, it show also irrevelant…
oganigl Jan 20, 2026
22a8e60
change head deps/stlib
oganigl Jan 23, 2026
224435c
change runes and some includes
oganigl Jan 23, 2026
381678d
change stlib deps
oganigl Jan 23, 2026
bcb5f62
some more changes to avoid errors
oganigl Jan 23, 2026
1b75e82
Merge remote-tracking branch 'origin/main' into hardfault/feature
oganigl Jan 23, 2026
7fc2c21
add new deps/stlib again
oganigl Jan 23, 2026
b71f934
added Examples and make sure that works with the MPUmanager
oganigl Jan 27, 2026
3f00ef1
Merge branch 'main' into hardfault/feature
oganigl Jan 27, 2026
27d1d38
change the asm name instruction
oganigl Jan 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ target_compile_options(${EXECUTABLE} PRIVATE
$<$<BOOL:${CMAKE_CROSSCOMPILING}>:-mfloat-abi=hard>
$<$<BOOL:${CMAKE_CROSSCOMPILING}>:-mthumb>
$<$<BOOL:${CMAKE_CROSSCOMPILING}>:-specs=nosys.specs>

-g
-ffunction-sections
-fdata-sections
-fno-exceptions
Expand Down
58 changes: 58 additions & 0 deletions Core/Src/Examples/ExamplesHardFault.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#ifdef EXAMPLE_HARDFAULT

#include "main.h"
#include "ST-LIB.hpp"

#ifdef TEST_MEMORY_FAULT
constexpr auto my_uint32_t = MPUDomain::Buffer<uint32_t>();

int main(void) {

Hard_fault_check();
STLIB::start();

using myBoard = ST_LIB::Board<my_uint32_t>;
myBoard::init();

[[maybe_unused]] auto my_buffer = myBoard::instance_of<my_uint32_t>().template as<my_uint32_t>();
my_buffer[1000000000] = 5;
while (1) {
STLIB::update();
}
}

#endif

#ifdef TEST_BUS_FAULT

int main(void) {
Hard_fault_check();
*(uint32_t*)0xdead0000 = 0x20;
STLIB::start();

using myBoard = ST_LIB::Board<>;
myBoard::init();

while (1) {
STLIB::update();
}
}

#endif

#ifdef TEST_USAGE_FAULT

int main(void) {
Hard_fault_check();
__builtin_trap();
STLIB::start();
using myBoard = ST_LIB::Board<>;
myBoard::init();

while (1) {
STLIB::update();
}
}

#endif
#endif
20 changes: 20 additions & 0 deletions Core/Src/config/leds_hard_fault.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#include "HALAL/HALAL.hpp"
extern "C"{

#ifdef NUCLEO
GPIO_TypeDef* ports_hard_fault[] = {GPIOB,GPIOB,GPIOE};
uint16_t pins_hard_fault[] = {GPIO_PIN_0,GPIO_PIN_14,GPIO_PIN_1};
// //don't touch the count
uint8_t hard_fault_leds_count = (sizeof(ports_hard_fault)/sizeof(GPIO_TypeDef*) == sizeof(pins_hard_fault)/sizeof(uint16_t))
? sizeof(pins_hard_fault)/sizeof(uint16_t) : 0;

#endif

#ifdef BOARD
GPIO_TypeDef* ports_hard_fault[] = {GPIOG,GPIOG,GPIOG,GPIOG};
uint16_t pins_hard_fault[] = {GPIO_PIN_13,GPIO_PIN_12,GPIO_PIN_11,GPIO_PIN_10};
// //don't touch the count
uint8_t hard_fault_leds_count = (sizeof(ports_hard_fault)/sizeof(GPIO_TypeDef*) == sizeof(pins_hard_fault)/sizeof(uint16_t))
? sizeof(pins_hard_fault)/sizeof(uint16_t) : 0;
#endif
}
29 changes: 9 additions & 20 deletions Core/Src/main.cpp
Original file line number Diff line number Diff line change
@@ -1,34 +1,23 @@
#define EXAMPLE_BASE
#define TEST_0 // Test to be run

// Include all examples, run the one defined above
#include "Examples/ExampleMPU.cpp"

#ifdef EXAMPLE_BASE
#include "Examples/ExamplesHardFault.cpp"

#include "main.h"
#include "ST-LIB.hpp"

int main(void) {
#ifdef SIM_ON
SharedMemory::start();
#endif

DigitalOutput led_on(PB0);
STLIB::start();
int main(void) {
Hard_fault_check();
STLIB::start();

Time::register_low_precision_alarm(100, [&]() { led_on.toggle();
});
using myBoard = ST_LIB::Board<>;
myBoard::init();

while (1) {
STLIB::update();
}
while (1) {
STLIB::update();
}
}

void Error_Handler(void) {
ErrorHandler("HAL error handler triggered");
while (1) {
}
}

#endif
215 changes: 189 additions & 26 deletions Core/Src/stm32h7xx_it.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
/* Includes ------------------------------------------------------------------*/
#include "main.h"
#include "stm32h7xx_it.h"
#include "stm32h7xx_hal.h"
#include "HALAL/HardFault/HardfaultTrace.h"
/* Private includes ----------------------------------------------------------*/
/* USER CODE BEGIN Includes */
/* USER CODE END Includes */
Expand Down Expand Up @@ -73,6 +75,15 @@ extern DMA_HandleTypeDef hdma_spi3_rx;
extern DMA_HandleTypeDef hdma_spi3_tx;
extern SPI_HandleTypeDef hspi3;
extern FDCAN_HandleTypeDef hfdcan1;
/*
Externs for calltrace
*/
extern uint32_t _stext;
extern uint32_t _etext;
extern uint32_t _sstack;
extern uint32_t _estack;
extern uint32_t _hf_stack_start;
extern uint32_t _hf_stack_end;
/* USER CODE BEGIN EV */

/* USER CODE END EV */
Expand All @@ -83,48 +94,200 @@ extern FDCAN_HandleTypeDef hfdcan1;
/**
* @brief This function handles Non maskable interrupt.
*/
void NMI_Handler(void)
{
/* USER CODE BEGIN NonMaskableInt_IRQn 0 */

/* USER CODE END NonMaskableInt_IRQn 0 */
/* USER CODE BEGIN NonMaskableInt_IRQn 1 */
while (1)
{
}
/* USER CODE END NonMaskableInt_IRQn 1 */
//calls my_fault_handler with the MSP(main stack pointer)
#define HARDFAULT_HANDLING_ASM() \
__asm__ __volatile__( \
/* Detect which stack was in use */ \
"tst lr, #4 \n" \
"ite eq \n" \
"mrseq r0, msp \n" \
"mrsne r0, psp \n" \
\
/* Switch to dedicated HardFault stack */ \
"ldr r1, =_hf_stack_end \n" \
"msr msp, r1 \n" \
"isb \n" \
\
/* Call C handler with original frame */ \
"b my_fault_handler_c \n" \
)


//create the space for the hardfault section in the flash
__attribute__((section(".hardfault_log")))
volatile uint32_t hard_fault[128];

void hardfault_flash_write(
uint32_t addr_hard_fault, const void *data_hard_fault, size_t len_hard_fault,
uint32_t addr_metadata, const void *data_metadata, size_t len_metadata)
{
__disable_irq();
HAL_FLASH_Unlock();

// Erase sector
FLASH_EraseInitTypeDef erase;
uint32_t sector_error = 0;
erase.TypeErase = FLASH_TYPEERASE_SECTORS;
erase.Banks = FLASH_BANK_1;
erase.Sector = FLASH_SECTOR_6;
erase.NbSectors = 1;
erase.VoltageRange = FLASH_VOLTAGE_RANGE_3;

if(HAL_FLASHEx_Erase(&erase, &sector_error) != HAL_OK){
__BKPT(0);
}


size_t offset, copy_len;
uint8_t block[32];

offset = 0;
while(offset < len_hard_fault){
memset(block, 0xFF, sizeof(block));
copy_len = (len_hard_fault - offset) > 32 ? 32 : (len_hard_fault - offset);
memcpy(block, (uint8_t*)data_hard_fault + offset, copy_len);

if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, addr_hard_fault + offset, (uint32_t*)block) != HAL_OK){
__BKPT(0);
}
offset += 32;
}

offset = 0;
while(offset < len_metadata){
memset(block, 0xFF, sizeof(block));
copy_len = (len_metadata - offset) > 32 ? 32 : (len_metadata - offset);
memcpy(block, (uint8_t*)data_metadata + offset, copy_len);

if(HAL_FLASH_Program(FLASH_TYPEPROGRAM_FLASHWORD, addr_metadata + offset, (uint32_t*)block) != HAL_OK){
__BKPT(0);
}
offset += 32;
}

SCB_InvalidateICache();
SCB_InvalidateDCache();

HAL_FLASH_Lock();
__enable_irq();
}
static uint8_t is_valid_pc(uint32_t pc)
{
pc &= ~1U; // Thumb
return (pc >= (uint32_t)&_stext &&
pc < (uint32_t)&_etext);
}
__attribute__((noreturn, optimize("O0")))
static void scan_call_stack(sContextStateFrame *frame, HardFaultLog *log_hard_fault)
{
uint32_t *stack_start = (uint32_t *)&_sstack;
uint32_t *stack_end = (uint32_t *)&_estack;

log_hard_fault->CallTrace.depth = 0;
uint32_t *sp = (uint32_t *)(frame + 1);
while (sp < stack_end && sp >= stack_start)
{
uint32_t val = *sp++;
if (log_hard_fault->CallTrace.depth >= CALL_TRACE_MAX_DEPTH) break;
if ((val & 1U) == 0) continue;
if (!is_valid_pc(val)) continue;
log_hard_fault->CallTrace.pcs[log_hard_fault->CallTrace.depth++] = val & ~1U;
}
}
__attribute__((noreturn,optimize("O0")))
void my_fault_handler_c(sContextStateFrame *frame) {
volatile uint32_t real_fault_pc = frame->return_address & ~1;
volatile HardFaultLog log_hard_fault;

volatile uint32_t *cfsr = (volatile uint32_t *)0xE000ED28;
//keep the log in the estructure
log_hard_fault.HF_flag = HF_FLAG_VALUE;
log_hard_fault.frame = *frame;
log_hard_fault.frame.return_address = real_fault_pc;
log_hard_fault.CfsrDecode.cfsr = *cfsr;
log_hard_fault.fault_address.Nothing_Valid = 0;

const uint8_t memory_fault = *cfsr & 0x000000ff;
if(memory_fault){
const uint8_t MMARVALID = memory_fault & 0b10000000; // We can find the exact place were occured the memory fault
const uint8_t MLSPERR = memory_fault & 0b00100000; // MemManage fault FPU stack
const uint8_t MSTKERR = memory_fault & 0b00010000; // Stack overflow while entring an exception
const uint8_t MUNSTKERR = memory_fault & 0b00001000; // Stack error while exiting from an exception (Corrupted stack)
const uint8_t DACCVIOL = memory_fault & 0b00000010; //Data access violation (acceded to pointer NULL, to a protected memory region, overflow in arrays ...)
const uint8_t IACCVIOL = memory_fault & 0b00000001; //Instruction access violation
if(MMARVALID){
uint32_t memory_fault_address = *(volatile uint32_t *)0xE000ED34;
log_hard_fault.fault_address.MMAR_VALID = memory_fault_address;
}
}
const uint8_t bus_fault = (*cfsr & 0x0000ff00) >> 8;
if(bus_fault){
const uint8_t BFARVALID = bus_fault & 0b10000000; // BFAR is valid we can know the address which triggered the fault
const uint8_t LSPERR = bus_fault & 0b00100000; //Fault stack FPU
const uint8_t STKERR = bus_fault & 0b00010000; // Fault stack while entring an exception
const uint8_t UNSTKERR = bus_fault & 0b00001000; // Stack error while exiting an exception
const uint8_t IMPRECISERR = bus_fault & 0b00000010; // Bus fault, but the instruction that caused the error can be uncertain
const uint8_t PRECISERR = bus_fault & 0b00000001; //You can read Bfar to find the eact direction of the instruction
if(BFARVALID){
volatile uint32_t bus_fault_address = *(volatile uint32_t *)0xE000ED38;
log_hard_fault.fault_address.BFAR_VALID = bus_fault_address;
//Don't trust in case IMPRECISERR == 1;
}
}
const uint16_t usage_fault = (*cfsr & 0xffff0000) >> 16;
if(usage_fault){
const uint16_t DIVBYZERO = usage_fault & 0x0200; // Div by ZERO hardfault;
const uint16_t UNALIGNED = usage_fault & 0x0100; // Unaligned access operation occured
const uint16_t NOCP = usage_fault & 0x0008; //Access to FPU when is not present
const uint16_t INVPC = usage_fault & 0x0004; //Invalid program counter load
const uint16_t INVSTATE = usage_fault & 0x0002; // Invalid processor state
const uint16_t UNDEFINSTR = usage_fault & 0x0001; //Undefined instruction.
}
if(usage_fault | bus_fault){
scan_call_stack(frame,&log_hard_fault);
}
volatile uint8_t metadata_buffer[0x100];
memcpy(metadata_buffer,(void*)METADATA_FLASH_ADDR,0x100);
//write log hard fault
hardfault_flash_write(HF_FLASH_ADDR,(uint8_t*)&log_hard_fault,sizeof(log_hard_fault),METADATA_FLASH_ADDR,&metadata_buffer,sizeof(metadata_buffer));
//reboot the system
volatile uint32_t *aircr = (volatile uint32_t *)0xE000ED0C;
__asm volatile ("dsb");
*aircr = (0x05FA << 16) | 0x1 << 2;
__asm volatile ("dsb");
while (1) {} // should be unreachable
}

/**
* @brief This function handles Hard fault interrupt.
*/
__attribute__((naked))
void HardFault_Handler(void)
{
/* USER CODE BEGIN HardFault_IRQn 0 */
HARDFAULT_HANDLING_ASM();
while (1){}
}

/* USER CODE END HardFault_IRQn 0 */
void NMI_Handler(void)
{
/* USER CODE BEGIN NonMaskableInt_IRQn 0 */
/* USER CODE END NonMaskableInt_IRQn 0 */
/* USER CODE BEGIN NonMaskableInt_IRQn 1 */
while (1)
{
/* USER CODE BEGIN W1_HardFault_IRQn 0 */
/* USER CODE END W1_HardFault_IRQn 0 */
}
/* USER CODE END NonMaskableInt_IRQn 1 */
}

/**
* @brief This function handles Memory management fault.
*/
void MemManage_Handler(void)
{
/* USER CODE BEGIN MemoryManagement_IRQn 0 */

/* USER CODE END MemoryManagement_IRQn 0 */
while (1)
{
/* USER CODE BEGIN W1_MemoryManagement_IRQn 0 */
/* USER CODE END W1_MemoryManagement_IRQn 0 */
extern void my_fault_handler_c(sContextStateFrame *frame);

__asm volatile(
"mrs r0, msp\n" // obtener stack frame
"b my_fault_handler_c\n"
);
}
}

/**
* @brief This function handles Pre-fetch fault, memory access fault.
*/
Expand Down
2 changes: 1 addition & 1 deletion deps/ST-LIB
Loading