« Previously: How to debug hard faults in ARM Cortex-M0 based SoCs  

When the hard fault occurs in such a scenario, the first step is to detect the cause of the hang up as explained in the section above. Once you ensure that this is a hard fault, the next step is to look at the call stack to get an idea of which function caused the violation, as illustrated in Figure 1.

Cypress hardfault 03 (cr) Figure 1: Registers and call stack window displayed in PSoC Creator IDE on a hard fault (Source: Cypress Semiconductor)

The call stack shows function in the sequence in which they were called. In this example, the sequence main() -> i2cRead() -> IntDefaultHandler(). IntDefaultHandler() is the hard fault handler. From this we can infer that an operation in the function i2cRead() caused the hard fault. Once you know the function that is causing the fault, review that section of code thoroughly to identify the issue. You can also now execute in debugging mode and do a step-by-step execution in that function, examining the variables for any violations using the watch window.

Another scenario where the core might execute an undefined instruction is when you try to execute a function with an undefined function pointer. This may happen when you try to access a function pointer from an array of function pointers beyond the defined array size.

Some best practices to avoid these kinds of faults are:

  • Always pass the size along with the pointer address into a function
  • In the function, check if the pointer and the size are valid. Do not access elements that are out of bound.

Attempted load or store to an unaligned address

This type of hard fault occurs when the core tries to read or write to an unaligned address. A common scenario is when you have a packed structure for communication with an external peripheral. Some of the structure elements may be on unaligned addresses. If one such address is accessed as a pointer in another function, this results in an unaligned access. An example is shown below:

#include <project.h> typedef struct attribute((packed)){ uint32 header; uint8 packetLength; uint16 buffer[64]; }i2cPacket_t; void parseData(uint16 *buf); int main() { i2cPacket_t i2cPacket;

for(;;) { parseData(&i2cPacket.buffer[0]); } } void parseData(uint16 *buf) { uint16 intBuf[64]; uint8 index = 0;

  for(index= 0; index < 64; index++)
  {
        intBuf[index] = buf[index];
  }

}

In the above example, the structure element buffer will be assigned an odd address because of the packed structure. When this buffer is accessed in the function parseData, it results in a hard fault. To avoid this type of error, ensure that 32-bit and 16-bit variables in a packed structure are always aligned. Use padding bytes if needed.

To debug this type of hard fault, halt execution and view the registers. If the XPSR register has the exception number as ‘3’, then it is a hard fault. View the call stack window to trace back and identify which function caused the violation. Review the code thoroughly and make the necessary fixes in the firmware.

Cypress hardfault 04 (cr) Figure 2: Registers and call stack window displayed in PSoC Creator IDE on a hard fault (Source: Cypress Semiconductor)

Execution of an instruction from an XN memory address XN is a memory designation for Execute Never. XN prevents the processor from accessing instructions in this region of memory. A hard fault exception is generated upon an attempt to execute an instruction fetched from an XN region of memory. To avoid the hard fault, you should ensure that the core is not fetching instructions from the regions marked as XN in the following table:

Cypress hardfault 05 (cr) Figure 3: Memory Regions (Source: Cypress Semiconductor)

The linker script has the definitions of all the memory regions. Ensure that the code region does not overlap with any of the regions marked with XN in the table above.

 
« Previously: How to debug hard faults in ARM Cortex-M0 based SoCs