FreeRTOS V10.2.0
LPC1517 - Cortex M3
This is a continuation of this thread: https://forums.freertos.org/t/hardfault-with-corrupt-strange-msp/11004 but I’ve opened a new thread because I have completely created a new project in trying to track this down and it sufficiently different I felt it warranted a new discussion.
In working on that problem I have created a completely minimalistic project that manifests the problem I have been seeing. In this project there is just a main thread, the idle thread, and a single queue. Again everything is done statically. main() sets up the Q, the main thread and a repetitive timer. The main thread waits for an item in the Q and toggles an LED. The timer interrupt simply puts and item (a uint32_t) into the Q. The net effect is a toggle of the LED when something goes through the Q, that’s all the project does, here is the code:
#define QUEUE_LENGTH_RX 10
#define TASK_STACK_SIZE_MAIN 256
static StaticTask_t _task_main;
static StackType_t _task_stack_main[ TASK_STACK_SIZE_MAIN ];
static TaskHandle_t _task_hdl_main;
static void main_task( void * pvParameters );
StaticQueue_t _queue_hdr_rx;
static uint8_t _queue_buf_rx[ QUEUE_LENGTH_RX * sizeof( uint32_t ) ];
QueueHandle_t _queue_hdl_rx;
uint32_t frame;
uint32_t main_event;
#define DISABLE_WRITE_BUFFER
//******************************************************************************
int main(void)
//******************************************************************************
{
#ifdef DISABLE_WRITE_BUFFER
SCnSCB->ACTLR |= SCnSCB_ACTLR_DISDEFWBUF_Msk;
#endif
SystemCoreClockUpdate();
Chip_GPIO_Init(LPC_GPIO);
Chip_GPIO_SetPinDIROutput(LPC_GPIO, GREEN_PORT, GREEN_PIN);
Chip_Clock_EnablePeriphClock(SYSCTL_CLOCK_RIT);
Chip_RIT_Init(LPC_RITIMER);
Chip_RIT_SetTimerIntervalHz(LPC_RITIMER, 6);
NVIC_SetPriority(RITIMER_IRQn, configLIBRARY_MAX_SYSCALL_INTERRUPT_PRIORITY);
Chip_RIT_Enable(LPC_RITIMER);
NVIC_EnableIRQ(RITIMER_IRQn);
_queue_hdl_rx = xQueueCreateStatic( QUEUE_LENGTH_RX,
sizeof( uint32_t ),
_queue_buf_rx,
&_queue_hdr_rx );
vQueueAddToRegistry(_queue_hdl_rx, "rx");
_task_hdl_main = xTaskCreateStatic(
main_task ,
"main_task",
TASK_STACK_SIZE_MAIN,
NULL,
(tskIDLE_PRIORITY + 1UL),
_task_stack_main,
&_task_main);
vTaskStartScheduler();
return 0 ;
}
//******************************************************************************
static void main_task( void* ctx )
//******************************************************************************
{
while(1){
xQueueReceive( _queue_hdl_rx , &main_event , portMAX_DELAY );
Chip_GPIO_SetPinToggle(LPC_GPIO, GREEN_PORT, GREEN_PIN);
}
}
//******************************************************************************
void RIT_IRQHandler(void)
//******************************************************************************
{
Chip_RIT_ClearIntStatus(LPC_RITIMER);
xQueueSendFromISR( _queue_hdl_rx, &frame, NULL );
}
This code will run no longer than 15 minutes and would always hard fault (in memcpy) until I added some asserts at the beginning of xQueueReceive & prvCopyDataFromQueue. With the asserts in place, it will hang at a failed assertion in prvCopyDataFromQueue. All the assertions do is verify that the passed in args are actually pointing to the correct place (the globals main_event & _queue_hdl_rx).
In xQueueReceive:
BaseType_t xQueueReceive( QueueHandle_t xQueue, void * const pvBuffer, TickType_t xTicksToWait )
{
BaseType_t xEntryTimeSet = pdFALSE;
TimeOut_t xTimeOut;
Queue_t * const pxQueue = xQueue;
configASSERT( pxQueue == (Queue_t*)&_queue_hdr_rx);
configASSERT( pvBuffer == (void*)&main_event);
In prvCopyDataFromQueue:
static void prvCopyDataFromQueue( Queue_t * const pxQueue, void * const pvBuffer )
{
configASSERT( pxQueue == (Queue_t*)&_queue_hdr_rx);
configASSERT( pvBuffer == (void*)&main_event);
After running a while, the first assert in prvCopyDataFromQueue fails. Moving up to the stack frame for xQueueReceive and examining the values yields this:
p {&_queue_hdr_rx, &main_event}
$19 = {0x2000794 <_queue_hdr_rx>, 0x2000814 <main_event>}
p {&xQueue, &pvBuffer}
$20 = {0x200032c <uxIdleTaskStack.9178+372>, 0x2000328 <uxIdleTaskStack.9178+368>}
p &pxQueue
$21 = (Queue_t * const *) 0x2000348 <_task_main+16>
p {xQueue, pxQueue}
$22 = {0x875 <prvTaskExitError>, 0x2000338 <_task_main>}
It can be seen from this that the 2 arguments to xQueueReceive, xQueue & pvBuffer are now pointing to locations in the idle task stack NOT the main task stack even though xQueueReceive is being called from the main task.
It is interesting to note that in this frame the SP is 0x2000740 and if we examine the memory there we see:
x/4w 0x2000740
0x2000740 <_task_stack_main+944>: 0xa5a5a5a5 0xffffffff 0x02000814 0x02000794
The last 3 words there are the arguments to xQueueReceive we would expect to see (portMAX_DELAY = 0xFFFFFFFF). Its probably worth mentioning at this point that MSP is corrupted as well with the value 0x2000fe0, it should be some where just below 0x2003000 - the end of RAM.
The RIT is the only interrupt active outside of FreeRTOS and its priority level is set correctly. I’m at a loss here. It appears that the wrong stack is being restored to the main task thread. Now I realize that 99% of the time it is user error, but I have no idea of what could be done wrong in this short little example.
Have I done something wrong in this example?
Why are these values so wacked out?
Why does it run for a while (sometimes fails at 5 minutes, sometimes 10, never gets past 15)?
How do I debug this further?