2
\$\begingroup\$

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.

\$\endgroup\$
  • \$\begingroup\$ I am absolutely a noob in C, but you declared a ptr of type uint_8t and you write numbers of uint32_t to its address, further you have an extern declaration of single uint8_t cell, where you acces it like an array via ptr use. \$\endgroup\$ – Marko Buršič Jul 11 at 12:03
  • \$\begingroup\$ Hey Marko, it is true there is 32 vs 8 difference but it should not affect how the code works. It is simply an artifact of my numerous experiments. I have fixed and retried it just in case and the result is still the same HardFault. \$\endgroup\$ – Stanislav Pankevich Jul 11 at 12:11
  • \$\begingroup\$ 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. After that, you could then look at the state of the relevant section in SCB->CFSR when one of these faults occur as well as SCB->MMFSR or SCB->BFAR to give you the faulting address. And +1 for going to the effort of creating a minimally reproducible example btw - we don't often see that here :P \$\endgroup\$ – brhans Jul 11 at 13:32
  • \$\begingroup\$ Are you absolutely sure that you have configured both the compiler and the linker with correct target architecture, so it know for which architecture to compile, and which libraries are correct for that architecture? You don't even say which compiler you are using. \$\endgroup\$ – Justme Jul 11 at 15:47
  • \$\begingroup\$ Try to perform the suspect write with an openocd command. Also try reads. Read the specs and make sure is really RAM at that address and make sure you are accessing the address you think you are. \$\endgroup\$ – Chris Stratton Jul 11 at 17:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.