Cortex A7 Entering Data Abort

I’m trying to run FreeRTOS on the main A7 core on an STM32MP157D-DK1 board, and I’ve successfully set up two blink tasks using an A9 port and they run just fine. However, I noticed that the Data Abort handler is being called three times (and only three times) with garbage addresses ex. 0x04B272C5 which is in reserved space. If I exit the data abort handler each time, the system seems to recover just fine and my two tasks run without any more exceptions. I ran GDB and found that the instruction responsible for this is “RFEIA sp!” in portRESTORE_CONTEXT. I also read the values off of SP and SP + 4 before the instruction, as well as the intended PC and processor status to restore and those all looked fine to me (SPSR = 0x1F, next PC = start of first task). I am also using the MMU and caches, disabling them doesn’t seem to help. I’m out of things to try at this point, any suggestions?

Does the SP looks within the range reserved for the processor mode? Can you try increasing the stack sizes for all the modes just for testing?

Sorry I should’ve mentioned this in the initial post, but I am using static memory and so I found that the sp points to a static array in the .bss segment, which is outside of the area allocated for user/system mode stack. Not sure if there is a different memory location i should be putting the static stack in besides that.

Here is my linker script:

OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm")
OUTPUT_ARCH(arm)
ENTRY(_Reset)

MEMORY
{
    ROM (rx) : ORIGIN = 0xC2000040, LENGTH = 0x0DFFFFC0
    RAM (rw): ORIGIN = 0xC0200000, LENGTH = 0x01D00000 
	HEAP (rw) : ORIGIN = 0xD0000000, LENGTH = 0x10000000
	SRAM1 (rwx) : ORIGIN = 0x10000000, LENGTH = 128K
	SRAM2 (rwx) : ORIGIN = 0x10020000, LENGTH = 128K
	SRAM3 (rwx) : ORIGIN = 0x10040000, LENGTH = 64K
	SRAM4 (rwx) : ORIGIN = 0x10050000, LENGTH = 64K
	SYSRAM (rw) : ORIGIN = 0x2FFC0000, LENGTH = 256K

}

__HEAP_SIZE = 0x40000;

SECTIONS
{
    .text : {
        *(.vector_table)
		*(.resethandler)
		*(.irqhandler)
		*(.text)
        *(.text*)

					/* Todo: check if we need the next 3 lines */
		*(.glue_7)         /* glue arm to thumb code */
		*(.glue_7t)        /* glue thumb to arm code */
		*(.eh_frame)

		KEEP (*(.init)) /* libc ctors */
		KEEP (*(.fini)) /* libc dtors */

        . = ALIGN(8);
     } > ROM

	/* .rodata sections (constants, strings, etc.) */
	.rodata :
	{
		. = ALIGN(8);
		*(.rodata)         
		*(.rodata*) 
		. = ALIGN(8);
	} > ROM

	/* used for unwinding (probably not used, but is ignored if your app doens't use exceptions */
	.ARM.extab	 : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >ROM
	.ARM : {
		__exidx_start = .;
		*(.ARM.exidx*)
		__exidx_end = .;
	} >ROM

	.preinit_array :
	{
		. = ALIGN(8);
		PROVIDE_HIDDEN (__preinit_array_start = .);
		KEEP (*(.preinit_array*))
		PROVIDE_HIDDEN (__preinit_array_end = .);
		. = ALIGN(8);
	} > ROM

	.init_array :
	{
		. = ALIGN(8);
		PROVIDE_HIDDEN (__init_array_start = .);
		KEEP (*(SORT(.init_array.*)))
		KEEP (*(.init_array*))
		PROVIDE_HIDDEN (__init_array_end = .);
		. = ALIGN(8);
	} > ROM

	.fini_array :
	{
		. = ALIGN(8);
		PROVIDE_HIDDEN (__fini_array_start = .);
		KEEP (*(SORT(.fini_array.*)))
		KEEP (*(.fini_array*))
		PROVIDE_HIDDEN (__fini_array_end = .);
		. = ALIGN(8);
	} > ROM


    _text_end = .;

    .data : AT(_text_end)
    {
        . = ALIGN(8);
        _data_start = .;
        *(.data)
        *(.data*)
        . = ALIGN(8);
        _data_end = .;
    } > RAM

    .bss : {
        . = ALIGN(8);
        _bss_start = .;
        *(.bss)
        *(.bss*)   /* required for some malloc calls */
		*(COMMON)  /* required for libc, such as __lock___atexit_recursive_mutex */
        . = ALIGN(8);
        _bss_end = .;
		. = ALIGN(256);
		_ram_aligned_end = .;
    } > RAM

	.heap (NOLOAD):
	{
		. = ALIGN(8);
		_sheap = .;
		. += __HEAP_SIZE;
		_eheap = .;
	} > HEAP

    _user_stack_start = _ram_aligned_end; 
    _user_stack_end = _user_stack_start + 0x10000;

    _svc_stack_start = _user_stack_end;
    _svc_stack_end = _svc_stack_start + 0x1000;

    _irq_stack_start = _svc_stack_end;
    _irq_stack_end = _irq_stack_start + 0x100; 

    _fiq_stack_start = _irq_stack_end;
    _fiq_stack_end = _fiq_stack_start + 0x100;

    _auxcore_user_stack_start = _fiq_stack_end;
    _auxcore_user_stack_end = _auxcore_user_stack_start + 0x1000;


  /* Remove information from the compiler libraries */
  /DISCARD/ :
  {
    libc.a ( * )
    libm.a ( * )
    libgcc.a ( * )
  }

	.ARM.attributes 0 : { *(.ARM.attributes) }
}

And my startup.s:

.syntax unified
.cpu cortex-a7

.equ MODE_FIQ, 0x11
.equ MODE_IRQ, 0x12
.equ MODE_SVC, 0x13
.equ MODE_ABT, 0x17
.equ MODE_UND, 0x1B
.equ MODE_SYS, 0x1F

.equ UART4_TDR, 0x40010028

.section .vector_table, "x"
.global _Reset
.global _start
_Reset:
    b Reset_Handler
    b Undef_Handler 								// 0x4  Undefined Instruction 
    b FreeRTOS_SWI_Handler 							// Software Interrupt 
    b PAbt_Handler  								// 0xC  Prefetch Abort
    b DAbt_Handler 									// 0x10 Data Abort
    b . 											// 0x14 Reserved 
    b FreeRTOS_IRQ_Handler 							// 0x18 IRQ 
    b FIQ_Handler 									// 0x1C FIQ

.section .text
Reset_Handler:
	cpsid   if 										// Mask Interrupts

	ldr r4, =UART4_TDR 								// UART: print 'A'
	mov r0, #65
	str r0, [r4]

	mrc     p15, 0, r0, c1, c0, 0					// Read System Control register (SCTLR)
	bic     r0, r0, #(0x1 << 12) 					// Clear I bit 12 to disable I Cache
	bic     r0, r0, #(0x1 <<  2) 					// Clear C bit  2 to disable D Cache
	bic     r0, r0, #0x1 							// Clear M bit  0 to disable MMU
	bic     r0, r0, #(0x1 << 11) 					// Clear Z bit 11 to disable branch prediction
	bic     r0, r0, #(0x1 << 13) 					// Clear V bit 13 to disable High Vector Table Base Address
	mcr     p15, 0, r0, c1, c0, 0 					// Write System Control register (SCTLR)
	isb
													// Configure ACTLR
	mrc     p15, 0, r0, c1, c0, 1 					// Read CP15 Auxiliary Control Register
	orr     r0, r0, #(1 <<  1) 						// Enable L2 prefetch hint 
	mcr     p15, 0, r0, c1, c0, 1 					// Write CP15 Auxiliary Control Register

													// Set Vector Base Address Register (VBAR) to point to this application's vector table
	ldr    r0, =0xC2000040
	mcr    p15, 0, r0, c12, c0, 0

    												// FIQ stack: Fill with FEFF
    msr cpsr_c, MODE_FIQ
    ldr r1, =_fiq_stack_start
    ldr sp, =_fiq_stack_end
    movw r0, #0xFEFF
    movt r0, #0xFEFF
fiq_loop:
    cmp r1, sp
    strlt r0, [r1], #4
    blt fiq_loop

   													// IRQ stack: Fill will F1F1
    msr cpsr_c, MODE_IRQ
    ldr r1, =_irq_stack_start
    ldr sp, =_irq_stack_end
    movw r0, #0xF1F1
    movt r0, #0xF1F1
irq_loop:
    cmp r1, sp
    strlt r0, [r1], #4
    blt irq_loop

   													// Supervisor (SVC) stack: Fill with F5F5
    msr cpsr_c, MODE_SVC
    ldr r1, =_svc_stack_start
    ldr sp, =_svc_stack_end
    movw r0, #0xF5F5
    movt r0, #0xF5F5
svc_loop:
    cmp r1, sp
    strlt r0, [r1], #4
    blt svc_loop

													// USER and SYS mode stack: Fill with F0F0
	msr cpsr_c, MODE_SYS
    ldr r1, =_user_stack_start
	ldr sp, =_user_stack_end
    movw r0, #0xF0F0
    movt r0, #0xF0F0
usrsys_loop:
    cmp r1, sp
    strlt r0, [r1], #4
    blt usrsys_loop

    												// Copying initialization values (.data)
    ldr r0, =_text_end
    ldr r1, =_data_start
    ldr r2, =_data_end

data_loop:
    cmp r1, r2
    ldrlt r3, [r0], #4
    strlt r3, [r1], #4
    blt data_loop

    												// Initialize .bss
    mov r0, #0
    ldr r1, =_bss_start
    ldr r2, =_bss_end

bss_loop:
    cmp r1, r2
    strlt r0, [r1], #4
    blt bss_loop

													// UART: print 'B'
	mov r5, #66
	str r5, [r4]

	bl SystemInit 									// Setup MMU, TLB, Caches, FPU, IRQ
    bl __libc_init_array 							// libc init (static constructors)

													// UART: print 'C'
	mov r5, #67
	str r5, [r4]

	cpsie  i                                        // enable irq interrupts

run_main:
    CPS     #0x13 //supervisor mode
    bl main
    b Abort_Exception

Abort_Exception:
	b .



SVC_Handler:
	b .

FIQ_Handler:
	b .

Hmmmm. Your handler is being called exactly three times, and there are three tasks in your system (your two tasks and the idle task). Could it be that there is a gap in your initial context restores? Shot in the dark, I know.,

Funny enough, I had the same exact thought and disabling both tasks now causes two faults. It seems like those two are always there. However, I had a variable i was using for debugging in one of my tasks and removing it decreased the number of faults by 1. So then I modified one task more to include a counter, and have the LED stop blinking after say 100 blinks (so the counter would be updating every loop). And it looks like the data fault is happening every time it tries to update/read that variable. Also, I looked into the DFSR and it seems to be an alignment fault. I’ll investigate some more in GDB and give some updates in a few hours

Ok I’ve kicked out the two led blink tasks just for simplicity, and now i’m only running the idle task. Here are the two data aborts that occur:

Fault status: 0x00001C06
Fault addr: 0x04B27245
lr value: 0xC2007270

Fault status: 0x00000801
Fault addr: 0x11111109
lr value: 0xC200727C

And the code at those addresses:

First, the first data abort triggers. Then, my breakpoint at prvIdleTask + 8 triggers. At this point, i check r11 as it was supposed to have been set by the previous add instruction, but it is still the uninitialized value 0x11111111. This explains the second data abort, which i can prevent from happening by setting the pc back one instruction and (re?)-executing that add instruction before continuing. The mystery that still remains here is what is triggering the first data abort? Looking at the fault status register here, it looks like it is some external asynchronous AXI Slave error. Another mystery to me is where that initial fault address of 0x04B27245 is coming from.

Setting bit 8 in portINITIAL_SPSR does the trick for me. According to this link, that bit will mask external aborts. At this point, it seems like this abort has nothing to do with freertos and more so to do with something specific to STM hardware, so I’ll mark this as solved

Thank you for sharing your solution!

One last update: I ended up switching my bootloader to u-boot and freeRTOS now works without that bit set. Looks like my previous bootloader didn’t set something up quite right. Again, nothing to do with FreeRTOS at this point.

1 Like