Prefetch Aborts within vTaskSuspend()

dspaude wrote on Tuesday, July 24, 2007:

Hello,

I constantly get Prefetch Aborts supposedly pointing to the memory range allocated to vTaskSuspend()–using preemption, by the way. I’ve tried increasing the heap size, the task stack sizes, etc. and nothing appears to work. I’m using the YAGARTO toolchain and an Atmel AT91SAM7SE. Do the ARM mode stack sizes set up in the assembly startup file make any difference? Any tips on things to try would be appreciated.

Thanks,
Darrik

davedoors wrote on Tuesday, July 24, 2007:

I just replied to your post on the other thread.  It may provide the answer you want.  If not write back.

Dave.

dspaude wrote on Tuesday, July 24, 2007:

Yes, I have read the memory configuration doc ( http://www.freertos.org/a00111.html ). I am using heap_2.c in my application. If I do NOT allow one of my tasks to be handled (which is resumed from an IRQ0 ISR using xTaskResumeFromISR() with the task handle), then most everything works okay. However, when I DO handle the task it will cause the Prefetch Abort at least at some point. I don’t know if it is the hierarchy of functions calls in the task (which I assumed could use up the stack, so I increased it to 2000 units = 8000 bytes with no change in behavior). So the only thing I could assume is that the PIT causes a context switch which maybe hits another task and causes ITS stack to overflow. I don’t know. I then tried to use the critical API calls in this particular task, but then it caused a Data Abort. I just can’t seem to win. Since the critical API calls seemed to be worse I removed those calls from within the task. Here’s my task which calls my ISR handler (a deferred interrupt handler where the interrupt is edge-triggered and only triggers once, but should get cleared when handled):

void my_InterruptTask(void *pvParameters)
{
   /* Just to stop compiler warnings. */
   ( void ) pvParameters;

   for( ;; )
   {
      vTaskSuspend( NULL );

//      portENTER_CRITICAL();

      my_HandleISR();

//      portEXIT_CRITICAL();
   }
}

rtel wrote on Wednesday, July 25, 2007:

I cannot see why the code you have posted should cause a prefetch abort, but don’t know what is going on in my_HandlerISR().  Prefetch abort would be very unusual.  Stack problems would normally result in a data abort.

I don’t think it is related to your problem, but depending on the rate at which your interrupts are arriving, there is the potential for a race condition when using the task suspend/resume mechanism in this manner.  Should another interrupt arrive prior to my_InterruptTask() calling vTaskSuspend() then the ISR will have attempted to resume the task before it was ever suspended.  This can result in the interrupt handling being missed as the task will eventually get around to calling vTaskSuspend() without anything telling it that an interrupt has already arrived.  Using a semaphore to wake a task from an ISR would fix this.

Regards.

dspaude wrote on Wednesday, July 25, 2007:

I delayed enabling the IRQ0 interrupt until after the majority of the code started up and that changed the behavior. Keep in mind I am using preemption (not cooperative). Now instead of the Prefetch Abort in vTaskSuspend() I am getting a Data Abort in xTaskRemoveFromEventList(). Since I wasn’t getting the second interrupt (as you thought might happen) I am trying the semaphore method. However, when I take the semaphore after the task has just been serviced, the OS goes to suspend the task (and it does), but then it fails to switch tasks and performs a data abort. Here is the code now:

void my_InterruptTask(void *pvParameters)
{
    /* Just to stop compiler warnings. */
    ( void ) pvParameters;

    for( ;; )
    {
      uart_TxString(AT91C_BASE_US0,"my_InterruptTask: wait\r\n"); // DJS test

//       vTaskSuspend( NULL );
       xSemaphoreTake( xOSSemaphoreHandle[S_MYIRQ], ( portTickType ) portMAX_DELAY );

//      portENTER_CRITICAL();

      uart_TxString(AT91C_BASE_US0,"my_InterruptTask: ISR\r\n"); // DJS test

      my_HandleISR();

//      portEXIT_CRITICAL();
   }
}

The result is the following:
my_InterruptTask: ISR
my_HandleISR: ulMyIrq=80000000
my_HandleISR: i=0
my_InterruptTask: wait
Data Abort at 0x2000469A 0xData Abort at 0x20000348 0xE5941000
Current task = MY_IRQ

0x2000469A is within xTaskRemoveFromEventList() and since the current task after the data abort is still MY_IRQ then it appears the switch to another ready task has not yet happened (the Data Abort strings and Current task strings are printed from the data abort ISR).

Any ideas?

davedoors wrote on Thursday, July 26, 2007:

I’m guessing you are, but I’ll ask the question anyway as I cannot see anything else wrong:  Are you using the xSemaphoreGiveFromISR() macro from within your interrupt?  ie the one with “FromISR” on the end.

dspaude wrote on Thursday, July 26, 2007:

Yes, I’m using the FromISR version. Here’s that code as well (this is an ARM-compiled file (not THUMB) and the handler is declared as “naked”):

void irq_Irq0ISR(void)
{
   /* IRQ0 ISR.  This can cause a context switch so is not defined as a
      standard ISR using the __irq keyword.  See the port documentation on the
      FreeRTOS.org website for more information. */

    /* This ISR can cause a context switch, so the first statement must be a
       call to the portENTER_SWITCHING_ISR() macro.  This must be BEFORE any
       variable declarations. */
    portENTER_SWITCHING_ISR();

   portBASE_TYPE xTaskWokenByPost = pdFALSE;

   /* Clear the edge-triggered interrupt */
   AT91C_BASE_AIC->AIC_ICCR = 0x1 << AT91C_ID_IRQ0;

//      xTaskWokenByPost = xTaskResumeFromISR( xMYIRQTaskHandle );
      xTaskWokenByPost = xSemaphoreGiveFromISR(xOSSemaphoreHandle[S_MYIRQ],xTaskWokenByPost);

    /* End the interrupt in the AIC. */
    AT91C_BASE_AIC->AIC_EOICR = 0;

    /* If a task was woken by either a character being received or a character
    being transmitted then we may need to switch to another task. */
    portEXIT_SWITCHING_ISR( xTaskWokenByPost );
}   

dspaude wrote on Thursday, July 26, 2007:

Okay, I believe that the prefetch aborts are the result of using printf-related calls. For example, I have a UART and a logging printf type of call and when those were being called during the task then FreeRTOS’s kernel would always blow up with the prefetch abort. My code looked like this:

   int cnt;
   char buffer[255];
   va_list arg_ptr;

   va_start(arg_ptr, msg);
   cnt = vsprintf(&buffer[0], msg, arg_ptr);
   va_end(arg_ptr);

I don’t know if an interrupt happening during this code caused the problem, but I am suspect of this since some of the prefetch aborts pointed to the tick handler.

Can anyone explain what is happening? I assume it must be related to how stack/heap is used for the variable parameter lists or printf code and that caused major chaos with FreeRTOS. Any ideas?

davedoors wrote on Friday, July 27, 2007:

A couple of things here.

First char buffer[255], is this declared on the stack?  If so, have you allocated that much stack to the task?  You might be better off declaring it static, but be careful of reentrancy problems if you do.

Second, any printf related GCC library is going to use masses of stack because the functions were never originally written for use in embedded systems.  There are some small implementations around that you could use instead.  FreeRTOS\Demo\CORTEX_LM3S6965_GCC\LuminaryDrivers\ustdlib.c might be of help, but Luminary might not be too happy if you use the code on an ARM7.

dspaude wrote on Friday, July 27, 2007:

It looks like it is a combination of using buffer[255] and the standard library’s vsprintf(). If I use vsprintf() using a buffer from a pool, then I don’t get the prefetch abort. When I use buffer[256] (rather than 255) and my own vsprintf() routine, then I don’t get the prefetch abort.

I have almost 2 MB of memory to use and my stacks are quite large, so 256 bytes isn’t that much. However, I figured I should try to narrow it down to the real offender here and it appears to be both. I think I will just use my own vsprintf() routine but still use the 256-byte in-stack buffer because in the routine using it I need speed and allocating a block of memory (and then freeing it) will add a lot of time.

Thanks for your input!