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.