FreeRTOS stack corruption on STM32F4 with gcc

anonymous wrote on Wednesday, February 20, 2013:

I’m trying to get FreeRTOS running on my stm32f4discovery board. I have installed summon-arm-toolchain and created a Makefile to compile my code. Here is the Makefile:

# Setting other include path...
BIN_DIR = $(CURDIR)/binary
vpath %.c  $(CURDIR)
# Setting other vpath...
vpath %.s $(STARTUP)
# Project Source Files
# FreeRTOS Source Files
# Other peripheral source files...
MCUFLAGS=-mcpu=cortex-m4 -mthumb -mfpu=fpv4-sp-d16 -mfloat-abi=softfp
LDFLAGS=$(COMMONFLAGS) -fno-exceptions -ffunction-sections -fdata-sections -nostartfiles -Wl,--gc-sections,-T$(LINKER_SCRIPT)
OBJ = $(SRC:%.c=$(BUILD_DIR)/%.o)
$(BUILD_DIR)/%.o: %.c
    $(CC) $(CFLAGS) $< -c -o $@
all: $(OBJ)
    $(AS) -o $(ASRC:%.s=$(BUILD_DIR)/%.o) $(STARTUP)/$(ASRC)
    $(CC) -o $(BIN_DIR)/$(TARGET).elf $(LDFLAGS) $(OBJ) $(ASRC:%.s=$(BUILD_DIR)/%.o) $(LDLIBS)
    $(OBJCOPY) -O ihex $(BIN_DIR)/$(TARGET).elf $(BIN_DIR)/$(TARGET).hex
    $(OBJCOPY) -O binary $(BIN_DIR)/$(TARGET).elf $(BIN_DIR)/$(TARGET).bin

I modified project in folder CORTEX_M4F_STM32F407ZG-SK of FreeRTOS Demo projects(removing the existing tasks and creating my own). Here is the main function:

int main(void) {
    int ret;
    DebugPrintf("FreeRTOS v7.3.0 starting\n");
    ret = xTaskCreate(SampleTask0, (signed char *) "T0", configMINIMAL_STACK_SIZE, NULL, 2, NULL);
    if (ret == pdTRUE) {
        DebugPrintf("Task %x creared successfully:%d.\n", SampleTask0, ret);
    } else {
        DebugPrintf("Task 0 created failed.\n");
    ret = xTaskCreate(SampleTask1, (signed char *) "T1", configMINIMAL_STACK_SIZE, NULL, 1, NULL);
    if (ret == pdTRUE) {
        DebugPrintf("Task %x creared successfully:%d.\n", SampleTask1, ret);
    } else {
        DebugPrintf("Task 1 created failed.\n");
    DebugPrintf("Starting scheduler...\n");
    for (;;);

I have configured configMINIMAL_STACK_SIZE as 4096 in FreeRTOSConfig.h and the code goes well as Task Scheduler started and invoked my SampleTask0 function. Here is the task code:

void SampleTask0(void *pvParameters) {
    (void) pvParameters;
    uint16_t delay;
    for (;;) {
        delay = 10000;
        DebugPrintf("Task 0 running\n");
        while(delay) {delay--;}

The Task 1 function is almost the same as Task 0 except it prints different information. These code compile and after I write the binary to my board, the SampleTask0 does not work as expected. The DebugPrintf function which sends character through USART3 only prints “Tas” and then everything halts. I traced the code with gdb and execute the code by step, “Task 0 running” got printed but when it returned to task function(before “while(delay) {delay-;}”) an error occurred:

Cannot access memory at address 0xa5a5a5a5

SampleTask0 (pvParameters=0x0) at main.c…

According to FreeRTOS documents, the stack of each task is filled with 0xa5 bytes upon creation. I think there may be something wrong with stack. I have set configCHECK_FOR_STACK_OVERFLOW to 2 to enable stack overflow detection, but my hook function had not been invoked when this happened.

The startup_stm32f4xx.s in CORTEX_M4F_STM32F407ZG-SK was created for EWARM toolchain and I replaced it with the startup file in STM32F4-Discovery_FW_V1.1.0 which I downloaded from ST website. So it could potentially corrupt the stack, but I’m not sure about this. Anyone have ideas about this?

rtel wrote on Wednesday, February 20, 2013:

Your defined stack size should be many times greater than necessary, so I don’t think stack overflow will be the issue.

Your tasks do not ever block.  This means that, because you are creating one task at priority 1 and the other task at priority 2, only the task at priority 2 will ever run.  I would suggest replacing the “while(delay) {delay-;}” loop with a call to vTaskDelay() - that will allow both tasks to execute.

Then there will be nothing to stop both tasks from attempting to print to the serial port at the same time, so you will need to use some kind of critical section around calls to DebugPrintf().  You cannot disable interrupts, or mask interrupts (using taskENTER/EXIT_CRITICAL()) because that might stop the UART from working.  Instead you could wrap the calls in vTaskSuspendAll(), xTaskResumeAll() calls to prevent context switching while in the DebugPrintf() function.

However, to start with I would recommend removing the DebugPrintf() calls.  Replace them with a simple variable increment, so

volatile uint_32_t ulTask1Inc = 0, ulTask2Inc = 0;
void vTask1( void *pvParameters )
    for( ;; )
        vTaskDelay( 100 );

and the same for Task 2 (but incrementing the other variable).  Let it run for a while then ensure both variables have incremented.

Most problems on STM32’s stem from incorrect priority asignments, or incorrect preemption/sub priority assignments, but it does not seem you are using any interrupts in your system yet so that cannot be your problem.


anonymous wrote on Thursday, February 21, 2013:

@richardbarry, thanks for your reply and part of this problem is solved.
1. Wrapping vTaskSuspendAll(), xTaskResumeAll() calls inside DebugPrintf prevents context switching and I can get the whole line now.
2. I remove DebugPrintf in the task code as you suggested. It seems that now task 0 (higher priority) keeps running as ulTask0Inc is increasing. And task 1 never runs because of lower priority.

void SampleTask0(void *pvParameters) {
  (void) pvParameters;
  for (;;) {

However, when I uncomment the vTaskDelay line to enable both tasks to run. Task 0 simply gets stuck at vTaskDelay. When I trace the code with GDB, the “Cannot access memory at address 0xa5a5a5a5” occurred again when vTaskDelay returned to the task function. I think maybe there’s something preventing a context switch. As there’s no official port for STM32F4 with gcc toolchain. I used the startup file and link script from the STM32F4 examples provided by ST. Is it a possible cause of this problem?


rtel wrote on Thursday, February 21, 2013:

Task 0 simply gets stuck at vTaskDelay

Can you explain what that means?  Do you mean that once vTaskDelay() is called the task never runs again?  Or that the MCU gets stuck somewhere inside vTaskDelay() and the other task never runs?  Or something else?

When I trace the code with GDB, the “Cannot access memory at address 0xa5a5a5a5”

Where is that message being displayed?

GDB will try and unwind the stack frame so it can be displayed in a debugger window.  It expects the stack to end at the stack of main(), and for all the stack memory to be in the stack segment as defined by the linker script.  However, when you run a task the stack of the task will have come from the FreeRTOS heap, and the top stack frame does not end up with the stack of main().  This can result in the GDB window showing this kind of error because it attempts to unwind past the end of the stack (where it will find 0xa5a5a5a5, which is the FreeRTOS stack fill byte).  Which version of FreeRTOS are you using?

Have you installed the interrupt handlers necessary to perform a context switch?  See point 1 here:


anonymous wrote on Friday, February 22, 2013:

@richardbarry, thanks for your reply. I’m using FreeRTOS V7.3.0 now.
For “Task 0 gets stuck at vTaskDelay”, I mean that Task 0 called vTaskDelay but the function never return. And if I traced into vTaskDelay with GDB, the function returned with the “Cannot access memory at address 0xa5a5a5a5”  error.
GDB output is like this if I continue:

(gdb) b SampleTask0
Breakpoint 1 at 0x8000860: file main.c, line 160.
(gdb) b SampleTask1
Breakpoint 2 at 0x800088c: file main.c, line 171.
(gdb) c
Note: automatically using hardware breakpoints for read-only addresses.
Breakpoint 1, SampleTask0 (pvParameters=0x0) at main.c:160
160	    ulTask0Inc++;
(gdb) n
162	    vTaskDelay(1);
(gdb) c

And like this if I traced into vTaskDelay:

814	  if (xAlreadyYielded == pdFALSE)
816	      [b]portYIELD_WITHIN_API[/b]();
(gdb) s
vPortYieldFromISR () at ../Source/portable/GCC/ARM_CM4F/port.c:274
(gdb) n
275	}
vTaskDelay (xTicksToDelay=1) at ../Source/tasks.c:818
818	}
Cannot access memory at address 0xa5a5a5a5
SampleTask0 (pvParameters=0x0) at main.c:163
163	  }

Please note that if I tried to step over portYIELD_WITHIN_API, it did not return. And if I step into then step out, it returned to vTaskDelay normally. However, then it returned to Task 0 with the “Cannot access memory” error, as you explained it’s caused by GDB. So it seems that there’s something wrong in portYIELD_WITHIN_API.

I checked my startup file and FreeRTOSConfig.h, seems that interrupt handlers list on the FAQ page have been installed. Here is the vector table:

  .word  _estack
  .word  Reset_Handler
  .word  NMI_Handler
  .word  HardFault_Handler
  .word  MemManage_Handler
  .word  BusFault_Handler
  .word  UsageFault_Handler
  .word  0
  .word  0
  .word  0
  .word  0
  .word  SVC_Handler
  .word  DebugMon_Handler
  .word  0
  .word  PendSV_Handler
  .word  SysTick_Handler

And I have added the following lines in FreeRTOSConfig.h:

#define vPortSVCHandler SVC_Handler
#define xPortPendSVHandler PendSV_Handler
#define xPortSysTickHandler SysTick_Handler

I have deleted the default handlers in stm32f4xx_it.c, and I have confirmed these handlers have been called by setting a breakpoint on them with GDB.


rtel wrote on Friday, February 22, 2013:

It would seem from what you have said that the code is as it should be, but the yield is not yielding.  If you run both tasks at the same priority, without a vTaskDelay(), do the counts in both tasks increment or just in one task?


anonymous wrote on Saturday, February 23, 2013:

@richardbarry, thanks for your reply.
I run both task at the same priority, without a vTaskDelay(). Now only Task 1 is executed.

(gdb) b SampleTask0
Breakpoint 1 at 0x8000818: file main.c, line 159.
(gdb) b SampleTask1
Breakpoint 2 at 0x800083c: file main.c, line 170.
(gdb) c
Note: automatically using hardware breakpoints for read-only addresses.
Breakpoint 2, SampleTask1 (pvParameters=0x0) at main.c:170
170	    ulTask1Inc++;
Breakpoint 2, SampleTask1 (pvParameters=0x0) at main.c:170
170	    ulTask1Inc++;
Breakpoint 2, SampleTask1 (pvParameters=0x0) at main.c:170
170	    ulTask1Inc++;

davedoors wrote on Saturday, February 23, 2013:

Sorry if this is a silly question, but when you step through code like that you will only see one task running because the debugger does not know that the whole micro context can get changed between lines of C code. You will have to look at the values of the two variables to know if the other task is running as well.

If only one task is running then a context switch is not happening in the tick interrupt. You have already said that a context switch is not happening when vTaskDelay() is called. On that chip the same software interrupt is used for both so it is likely that the software interrupt is not executing. It might be that no interrupts are executing.

You said earlier that one of the RTOS interrupts was being called, but which one? The SVC handler must execute because the tasks start running, so that indicates the interrupt handlers are installed (SVC is only used once at the start). Can you put a break point in xPortPendSVHandler() and another in xPortSysTickHandler() in port.c to see which get called. xPortSysTickHandler() is called on each tick but does not actually do the context switching, that is done from cxPortPendSVHandler().

anonymous wrote on Saturday, February 23, 2013:

@davedoors, thanks for your reply.
I have checked the value of the two variables and I am sure only one task is running without vTaskDelay().
Then I add vTaskDelay() in my tasks and all the three handlers are called as you said. However, I traced into xPortPendSVHandler() and find taskSECOND_CHECK_FOR_STACK_OVERFLOW() triggered an error.

HardFault_Handler () at stm32f4xx_it.c:54
54	{

So I set configCHECK_FOR_STACK_OVERFLOW to 1 and the context switching works. Both tasks are running.
I still cannot figure out why stack overflow check makes context switching not working.


rtel wrote on Sunday, February 24, 2013:

I would appreciate it if you could clarify your comments here so I can suggest what your problem might be.

Does the context switch work when:

1) configCHECK_FOR_STACK_OVERFLOW is set to 0?

2) configCHECK_FOR_STACK_OVERFLOW is set to 1?

3) configCHECK_FOR_STACK_OVERFLOW is set to 2?

If the answer is no to any of the above, at which point in the context switch does it get stuck.

triggered an error.

By triggers an error, do you mean it calls the stack overflow hook?


anonymous wrote on Monday, February 25, 2013:

@richardbarry, thanks for your reply.
As you can see from the gdb output, after calling taskSECOND_CHECK_FOR_STACK_OVERFLOW(), it went to HardFault_Handler() which indicates a invalid memory access or stack overflow. I take a look at taskSECOND_CHECK_FOR_STACK_OVERFLOW() and find it called memcmp(line 149 in StackMacro.h), which is a function in C library. However, the tool chain I use(summon-arm-toolchain) , is for bare bone ARM processor software, with no libc. I replaced the memcmp with a function I implemented myself, both tasks run well.
Some commercial GCC distributions like Atollic contain the Newlib as the C library, but others do not. So in my humble opinion, could you please add some check during compilation of FreeRTOS, to check if libc exists or not? At least give an error message when there’s no libc.


davedoors wrote on Monday, February 25, 2013:

could you please add some check during compilation of FreeRTOS, to check if libc exists or not

There is no need to do that because in any application, whether it uses FreeRTOS or not, if the source code calls a function that does not exist then the project will not link (it will compiler, but it won’t link). Either you application was never actually building (which can’t be true if you were able to debug it) or memcmp() is defined somewhere in your code, probably just as a stub. This issue is completely related to your build environment, and not FreeRTOS. You can get pre-built ARM GCC toolchains from lots of places. ARM themselves develop support and distribute one.

(Newlib is not a good choice for Embedded tiny systems)