BusFault after multiple pvPortMalloc calls

dueringa wrote on Wednesday, September 04, 2013:


I’m currently developing an application on an Cortex-M3 (STM32F103ZG) using FreeRTOS 7.5.0 (non-MPU). The memory allocation implementation used was heap2, switched to heap4 now, but the following error still occurs.

The IDE used is Keil µVision with MDK-ARM toolchain 4.21 (C compiler
The application consists of about 20 tasks.

Under certain circumstances, a task does some extensive data operations. This includes allocating (quite big) memory blocks (one is 8K), reading in a file from SD card and freeing the block again after processing. According to xPortGetFreeHeapSize, after freeing, the memory is available again (but what about fragmentation?). This task *always* does the memory allocating and freeing. But the file reading is only done under the circumstances.
After these operations, uxTaskGetStackHighWaterMark reports 100 byte (words?) minimal stack remaining for this task.

After the task has finished the operations, a context switch to another task occurs, which also tries to allocate some memory for a buffer. This is when the BusFault occurs. The bus fault does not occur there when the previous task doesn’t do these extensive operations.

In the BusFault_Handler, Keil reports the following (Fault reports window):

BUS_FAULT_ADDR: 0x00312800 (What does that tell me anyway?)

LR in exception handler: 0xfffffffd (bit 2 = 1, Process stack? According to ARM appnote 209)
PSP: 0x20009318

Memory window:

(R0) 0x20009318: 0x00312800
(R1) 0x2000931C: 0x20009A78
(R2) 0x20009320: 0x00000000
(R3) 0x20009324: 0x00312800
(R12) 0x20009328: 0x00000000
(LR) 0x2000932C: 0x0802F619 (unaligned address? now that's interesting)
(PC) 0x20009330: 0x0802F25A
(PSR) 0x20009334: 0x81000000

The PC address is inside heap_4.c:

   343:         for( pxIterator = &xStart; pxIterator->pxNextFreeBlock < pxBlockToInsert; pxIterator = pxIterator->pxNextFreeBlock ) 
   344:         { 
   345:                 /* Nothing to do here, just iterate to the right position. */ 
   346:         } 
   348:         /* Do the block being inserted, and the block it is being inserted after 
   349:         make a contiguous block of memory? */    
0x0802F254 4816      LDR      r0,[pc,#88]  ; @0x0802F2B0
0x0802F256 E000      B        0x0802F25A
0x0802F258 6800      LDR      r0,[r0,#0x00]
0x0802F25A 6803      LDR      r3,[r0,#0x00]
0x0802F25C 428B      CMP      r3,r1
0x0802F25E D3FB      BCC      0x0802F258
   350:         puc = ( unsigned char * ) pxIterator; 

LR  address is also inside heap_4.c:

   223:                                         xFreeBytesRemaining -= pxBlock->xBlockSize; 
   225:                                         /* The block is being returned - it is allocated and owned 
   226:                                         by the application and has no "next" block. */ 
0x0802F618 490C      LDR      r1,[pc,#48]  ; @0x0802F64C
0x0802F61A 6860      LDR      r0,[r4,#0x04]
0x0802F61C 6809      LDR      r1,[r1,#0x00]
0x0802F61E EBA10000  SUB      r0,r1,r0
0x0802F622 490A      LDR      r1,[pc,#40]  ; @0x0802F64C
0x0802F624 6008      STR      r0,[r1,#0x00]
   227:                                         pxBlock->xBlockSize |= xBlockAllocatedBit; 

Can somebody give me advice what to do about this? The unaligned address in the saved LR makes me very suspicious, but where does it come from?

Thanks in advance!


#define configUSE_PREEMPTION		                1
#define configUSE_TICKLESS_IDLE                     0
#define configCPU_CLOCK_HZ                          72000000UL
#define configTICK_RATE_HZ                          100
#define configMAX_PRIORITIES                        5
#define configMINIMAL_STACK_SIZE                    128
#define configTOTAL_HEAP_SIZE                       ( 45 * 1024 )
#define configMAX_TASK_NAME_LEN                     6
#define configUSE_16_BIT_TICKS                      0
#define configIDLE_SHOULD_YIELD                     1
#define configUSE_MUTEXES                           0
#define configUSE_RECURSIVE_MUTEXES                 0
#define configUSE_COUNTING_SEMAPHORES               0
// don't use this feature
#define configQUEUE_REGISTRY_SIZE                   0
#define configUSE_QUEUE_SETS                        0
/* Hook function related definitions. */
#define configUSE_IDLE_HOOK			                0
#define configUSE_TICK_HOOK			                0
#define configCHECK_FOR_STACK_OVERFLOW              2
// If used, must be void vApplicationMallocFailedHook( void );
#define configUSE_MALLOC_FAILED_HOOK                0
#ifdef MYDEBUG
#define configGENERATE_RUN_TIME_STATS               1
#define configUSE_TRACE_FACILITY                    1
//#include "trcKernelPort.h"
extern unsigned long uiOverflowCounter;
void App_Trace_vActivateTimer(void);
#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS()    {App_Trace_vActivateTimer();}
#define portGET_RUN_TIME_COUNTER_VALUE()            uiOverflowCounter
/* Co-routine definitions. */
#define configUSE_CO_ROUTINES                       0
#define configMAX_CO_ROUTINE_PRIORITIES             2
/* Software timer related definitions. */
// do not use freertos software timers
#define configUSE_TIMERS                            0
/* Interrupt nesting behaviour configuration. */
/* This is the raw value as per the Cortex-M3 NVIC.  Values can be 255
(lowest) to 0 (1?) (highest). */
#define configKERNEL_INTERRUPT_PRIORITY             255
// Syscall priority - all INTs with higher prio (==lower number) can NOT use FreeRTOS API!
#define configMAX_SYSCALL_INTERRUPT_PRIORITY        (8<<4) //only 4 bits  //NEVER! 0 /* No IRQs will interrupt the criticals anymore*/
/* This is the value being used as per the ST library which permits 16
priority values, 0 to 15.  This must correspond to the
configKERNEL_INTERRUPT_PRIORITY setting.  Here 15 corresponds to the lowest
NVIC value of 255. */
// Same name
#define configMAX_API_CALL_INTERRUPT_PRIORITY       15
/* Define to trap errors during development. */
//#define configASSERT( ( x ) )     if( ( x ) == 0 ) vAssertCalled( __FILE__, __LINE__ )
/* Set the following definitions to 1 to include the API function, or zero
to exclude the API function. */
#define INCLUDE_vTaskPrioritySet		            0
#define INCLUDE_uxTaskPriorityGet		            0
#define INCLUDE_vTaskDelete				            0
#define INCLUDE_vTaskSuspend                        1
#define INCLUDE_xResumeFromISR                      0
#define INCLUDE_vTaskDelayUntil                     0
#define INCLUDE_vTaskDelay                          1
#define INCLUDE_xTaskGetSchedulerState              0
#define INCLUDE_xTaskGetCurrentTaskHandle           0
#define INCLUDE_xTaskGetIdleTaskHandle              0
#define INCLUDE_xTimerGetTimerDaemonTaskHandle      0
#define INCLUDE_pcTaskGetTaskName                   0
#define INCLUDE_eTaskGetState                       0
#define INCLUDE_uxTaskGetStackHighWaterMark 1
#define INCLUDE_uxTaskGetStackHighWaterMark 1
#endif /* FREERTOS_CONFIG_H */

rtel wrote on Wednesday, September 04, 2013:

Why do you have configASSERT() commented out - it is there to help you catch problems in your code.

Are you using the STM32 peripheral library from ST?  If so, then before spending time looking into this, I recommend upgrading to FreeRTOS V7.5.2, uncommenting configASSERT() and then running the same application.  V7.5.2 contains code specifically for users of the ST peripheral libraries, which have some special Cortex-M3 configuration requirements (unlike any other dirvers we have encountered, they require you to change the Cortex-M3 configuration from its power up default).  See http://www.freertos.org/History.txt

Try that then get back to us to let us know if it caught a (Cortex-M3) configuration issue.  If not, we can try to diagnose your problem.


dueringa wrote on Wednesday, September 04, 2013:

Hello Richard,

thanks for your reply.
I upgraded to FreeRTOS 7.5.2 and uncommented configASSERT, creating a function so I can inspect the file and line number.

Upgrading fixed the assert in PortValidateInterruptPriority (line 650) I got in v7.5.0.  And yes, I’m using the STM32PeriphLib (and thus calling NVIC_PriorityGroupConfig(NVIC_PriorityGroup_4); before scheduler starts)

Now I don’t get configASSERTs anymore.

Indeed, the problem occurs when actually reading the file into the buffer. If I don’t write anything into the buffer, everything works fine.
The buffer I’m allocating memory for is a struct (well, an array of them) consisting of multiple char-arrays, each struct is 36 byte in size. I’m using configASSERT now to check if I write out of the buffer and/or the character arrays, but that doesn’t happen.

Actually, the sizes of the character array were kind of “uneven” (10, 5, 3, 4, 6, 3, 5 Bytes). I just changed it to (10, 6, 4, 4, 6, 4, 6), resulting in a total of 40 bytes per struct. And now it works fine, the error is gone.
Looks like it was an alignment problem?