I have to work with code written by someone else for the Cortex M3-based board. I know that this code is supposed to work and it used to pass its integration test: the code is flashed to the board using openocd
and then a Python script asserts that certain breakpoints are hit in the code using pygdbmi
.
Now in my environment, the integration test is failing and manually flashing the board and running the executable I am hitting a BusFault_Handler
every time the code is writing to RAM memory, a specific part of it allocated by the linker script.
This is a minimal C code that reproduces the hard fault. I have derived this minimal code from the real code that does memcpy
to that specific part of RAM (in the real program the writes to destination address by the memcpy
trigger the hard fault). My simplified code starts at the beginning of main
:
extern uint8_t __dedicated_ram_start;
int main(void) {
/// As suggested in the comments:
/// Try enabling Usage, Bus & Memory faults in the SCB->SHCSR register.
/// With those bits set to disabled (the default setting), everything
/// vectors to the HardFault handler instead of to the individual
/// handlers for each one.
SCB->SHCSR |= SCB_SHCSR_USGFAULTENA_Msk
| SCB_SHCSR_BUSFAULTENA_Msk
| SCB_SHCSR_MEMFAULTENA_Msk;
uint8_t *ptr = (uint8_t *)&__dedicated_ram_start;
for (uint32_t i = 0; i < 2000; i++) {
ptr[i] = i; // BusFault_Handler here, i is rather random in the range of [0..40]
}
...
These are parts that I see relevant in the linker script:
RAM_START_ADDRESS = 0x20000000;
RAM_SIZE = 64k;
DEDICATED_RAM_SIZE = 36k;
MEMORY
{
rom (rx) : ORIGIN = 0x60000000, LENGTH = 256k
/* Area of RAM usable for heap and stack */
ram (rwx) : ORIGIN = RAM_START_ADDRESS + DEDICATED_RAM_SIZE, LENGTH = RAM_SIZE - DEDICATED_RAM_SIZE
}
PROVIDE (__dedicated_ram_start = RAM_START_ADDRESS);
The bus fault handler:
(gdb) c
Continuing.
^C
Program received signal SIGINT, Interrupt.
BusFault_Handler () at ../.../startup-cortex-m3-minimal.S:328
312 B .
(gdb) bt
+bt
#0 HardFault_Handler () at ../.../startup-cortex-m3-minimal.S:312
#1 <signal handler called>
#2 0x6000036c in main () at ../.../main.cpp:155
(gdb) up
+up
#1 <signal handler called>
(gdb) up
+up
#2 0x6000036c in main () at ../.../main.cpp:155
155 ptr[i] = i;
(gdb) p i
+p i
$4 = 20
The value of i
in the above example is a different number with every run and
varies from 0 to 40.
I know this is very limited information that I am providing and I am happy to provide more if needed.
To the best of my knowledge, I am using the same board and the same toolchain to build the code as my predecessors because I was literally following their tutorial documents.
Here's the compiler version:
$ arm-none-eabi-gcc --version
arm-none-eabi-gcc (GNU MCU Eclipse ARM Embedded GCC, 64-bit) 8.2.1 20181213 (release) [gcc-8-branch revision 267074]
Copyright (C) 2018 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Update 1
One comment suggested to enable Bus/Usage/Mem Fault handlers and inspect corresponding registers. I am set the SCB->SHCSR
register and that transformed my original Hard Fault into a Bus Fault.
To turn imprecise errors into precise errors I am using
set *(uint32_t*)0xE000E008=(*(uint32_t*)0xE000E008 | 1<<1)
as recommended in How to debug a HardFault on an ARM Cortex-M MCU.
Using the debugging hard/bus fault handlers described here I am getting the following results:
Continuing.
Program received signal SIGTRAP, Trace/breakpoint trap.
HardFault_Handler () at .../main.cpp:149
149 bkpt();
(gdb) i loc
+i loc
faultType = BusFault
faultAddress = 536870940
isFaultPrecise = true
isFaultImprecise = false
isFaultOnUnstacking = false
isFaultOnStacking = false
isFaultAddressValid = true
(gdb) up
+up
#1 <signal handler called>
(gdb) up
+up
#2 0x6000036c in main () at ../.../main.cpp:155
155 ptr[i] = i;
(gdb) p i
+p i
$4 = 28
(gdb) p /x 28
+p /x 28
$6 = 0x1c
(gdb) p /x 536870940
+p /x 536870940
$8 = 0x2000001c
so the address fault seems to be exactly the one that corresponds to the last i
before the Bus Fault occurs.
Update 2
Another comment suggested trying reads as well. I tried the same for loop as above but this time doing reading from the ptr
memory and it crashes with the same BusFault_Handler
.