HardFault Handler on SIM3U167 (Cortex M3) when calling vTaskNotifyGiveFromISR

tantonini wrote on Saturday, June 06, 2015:

Hello everybody :slight_smile:

I’m developping a firmware for a SIM3U167 and I’m trying to implement FreeRTOS 8.2.0 on this one.

To summary, my firmware uses the ADC of my µC and I want to notify a task when I am in the scan done’s interruption routine. This is my problem : when I want to increase the speed of my ADC, I go in an HardFault Handler when I want to call the function vTaskNotifyGiveFromISR in my interruption.

When I go in the SARADC0_scan_done_handler over 1 kHz (approximately), it crashes. I don’t know if I have a problem with priorities, if I try to switch contexts to quickly for FreeRTOS or if it is an other problem.

I use the IDE furnished by Silabs : Precision32

I have cut the code to show you only principal things (sorry I don’t know how to color C language)

int main()
{
    extern uint8_t start;

    extern TaskHandle_t xTaskPotentiometers;

    // Enter the default operating mode for this application (gModes.c)
    enter_default_mode_from_reset();
    NVIC_SetPriority(SARADC0_IRQn, 100);

    xTaskCreate(vTaskPotentiometers, "pot", 200, NULL, 1, &xTaskPotentiometers);

    // Initialisation
    mySARADC0_init();
    
    /* Start the scheduler running the tasks and co-routines just created. */
    vTaskStartScheduler();

    while(1);
    // Loop forever...
}
void SARADC0_conv_complete_handler(void)
{
    SI32_SARADC_A_clear_single_conversion_complete_interrupt(SI32_SARADC_0);
}

void SARADC0_scan_done_handler(void)
{
    uint8_t k=0;

    extern TaskHandle_t xTaskPotentiometers;
    BaseType_t xHigherPriorityTaskWoken = pdFALSE;

    SI32_SARADC_A_clear_single_conversion_complete_interrupt(SI32_SARADC_0);
    SI32_SARADC_A_clear_scan_done_interrupt(SI32_SARADC_0);
    SI32_SARADC_A_disable_autoscan(SI32_SARADC_0);
    SI32_SARADC_A_enable_autoscan(SI32_SARADC_0);

    for (k = 0; k < 8; k++)
    {
        adc_read[k] = SI32_SARADC_A_read_data(SI32_SARADC_0);
    }

    vTaskNotifyGiveFromISR(xTaskPotentiometers, &xHigherPriorityTaskWoken);    <--------------BUG

    portYIELD_FROM_ISR( xHigherPriorityTaskWoken );
}
void vTaskPotentiometers(void *pvParameters)
{
    extern SemaphoreHandle_t xMutexUsb;

    uint32_t adc_output_old[8], adc_output[8];
    uint32_t adc_output_old128[8], adc_output128[8];

    uint32_t adc_value_send[8];
    uint8_t adc_number_send[8];
    extern uint32_t adc_read[8];

    uint8_t first_conversion = 1;
    uint8_t index_adc = 0;

    SI32_TIMER_A_start_high_timer(SI32_TIMER_0);

    for( ;; )
    {
        ulTaskNotifyTake( pdTRUE,           /* Clear the notification value before exiting. */
                portMAX_DELAY );            /* Block indefinitely. */

        index_adc = 0;

        for (uint8_t i = 0; i < 8; i++)
        {
            if (first_conversion == 1)
            {
                adc_output_old[i] = 0;
                adc_output_old128[i] = 0;

                adc_output[i] = adc_read[i] >> 2;
                adc_output128[i] = (adc_output[i]*8388)>>16;

                if (adc_output128[i] >= 128)
                {
                    adc_output128[i] = 127;
                }

                adc_number_send[index_adc] = i;
                adc_value_send[index_adc] = adc_output128[i];
                index_adc++;

                if (i == 7)
                {
                    first_conversion = 0;
                }
            }
            else
            {
                adc_output_old[i] = adc_output[i];
                adc_output_old128[i] = adc_output128[i];

                adc_output[i] = (adc_read[i] >> 2) + (adc_output_old[i]*3/4);
                adc_output128[i] = (adc_output[i]*8388)>>16;

                if (adc_output128[i] >= 128)
                {
                    adc_output128[i] = 127;
                }

                if (adc_output128[i] != adc_output_old128[i])
                {
                    adc_number_send[index_adc] = i;
                    adc_value_send[index_adc] = adc_output128[i];
                    index_adc++;
                }
            }
        }

        if (index_adc != 0)
        {
            xSemaphoreTake(xMutexUsb, 0);
            while(index_adc > 0)
            {
                SI32_USBEP_A_write_fifo_u8(SI32_USB_0_EP1, 0x01);
                SI32_USBEP_A_write_fifo_u8(SI32_USB_0_EP1, adc_number_send[index_adc-1]);
                SI32_USBEP_A_write_fifo_u8(SI32_USB_0_EP1, (uint8_t) adc_value_send[index_adc-1]);
                index_adc--;
            }
            xSemaphoreGive(xMutexUsb);
            vTaskResume(xTaskSendUSBControls);
        }
        index_adc = 0;
    }
}

I also show you the FreeRTOSConfig.h

#define configUSE_PREEMPTION        1
#define configUSE_IDLE_HOOK         0
#define configUSE_TICK_HOOK         0
#define configCPU_CLOCK_HZ          ( ( unsigned long ) 48000000 )
#define configTICK_RATE_HZ          ( ( TickType_t ) 1000 )
#define configMINIMAL_STACK_SIZE    ( ( unsigned short ) 70 )
#define configTOTAL_HEAP_SIZE       ( ( size_t ) ( 7000 ) )
#define configMAX_TASK_NAME_LEN     ( 10 )
#define configUSE_TRACE_FACILITY    0
#define configUSE_16_BIT_TICKS      0
#define configIDLE_SHOULD_YIELD     0
#define configUSE_CO_ROUTINES       0

#define configUSE_PORT_OPTIMISED_TASK_SELECTION     1

#define configMAX_PRIORITIES        ( 5 )
#define configMAX_CO_ROUTINE_PRIORITIES ( 2 )

#define configUSE_MUTEXES   1
/* 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             1
#define INCLUDE_vTaskCleanUpResources   0
#define INCLUDE_vTaskSuspend            1
#define INCLUDE_vTaskDelayUntil         1
#define INCLUDE_vTaskDelay              1
#define INCLUDE_xTaskGetSchedulerState  1

#define configKERNEL_INTERRUPT_PRIORITY         255
/* !!!! configMAX_SYSCALL_INTERRUPT_PRIORITY must not be set to zero !!!!
See http://www.FreeRTOS.org/RTOS-Cortex-M3-M4.html. */
#define configMAX_SYSCALL_INTERRUPT_PRIORITY    95 /* equivalent to priority 5. */



#endif /* FREERTOS_CONFIG_H */

I am a new user of FreeRTOS and new on the forum, if information are missing, don’t hesitate to ask me.

Thank you in advance for your help :slight_smile: (sorry if there are mistakes in my English, I’m French ^^)

Thomas

heinbali01 wrote on Saturday, June 06, 2015:

Bonjours Thomas,

Salut mec, merci bien pour ton message :slight_smile:

There is one thing that surprises me, the tick-count of zero that you use here:

    xSemaphoreTake(xMutexUsb, 0);
    while(index_adc > 0)
    {
        ...
        index_adc--;
    }
    xSemaphoreGive(xMutexUsb);
    vTaskResume(xTaskSendUSBControls);

At least I would expect code like:

    if( xSemaphoreTake(xMutexUsb, 0) != pdFALSE )
    {
        while(index_adc > 0)
        {
            ...
            index_adc--;
        }
        xSemaphoreGive(xMutexUsb);
    }
    else
    {
        /* failed to take the semaphore. */
    }

I can not understand why the call vTaskNotifyGiveFromISR() would lead to an exception.

( I don’t like HardFault or exception handlers at all. Debuggers can not show from where they’re called because the stack pointer is different. You must inspect a stack that is stored in some register )

Is it exactly above 1 KHz that things go wrong? Or does the probability increase along with the frequency?

My guess is that something goes wrong at a higher frequency because your xMutexUsb can not be taken. Your USB task is still busy doing thing. Could that be?

Regards,
Hein

rtel wrote on Saturday, June 06, 2015:

Agree with Hein’s comment regarding the semaphore. You would need to either:

  1. Use an infinite block time, so xSemaphoreTake() only returns when it has the semaphore, so you don’t need to check the function’s return value. Note infinite block times are used all over the place in example code, but are generally not a good idea in production code as it does not allow for error recovery should the semaphore never arrive.

  2. Use a finite block time, then check the return value of xSemaphoreTake() to determine if the semaphore was obtained or not - as per Hein’s code.

…however, I am suspicious of the line NVIC_SetPriority(SARADC0_IRQn, 100); If this is the CMSIS function the second parameter looks wrong, as it will expect an unshifted value. This is a common err on Cortex-M (in fact, on all Cortex) where the interrupt priorities are very confusing. This page tries to explain it, but may also confuse ;o) http://www.freertos.org/RTOS-Cortex-M3-M4.html

Do you have configASSERT() defined? I would strongly recommend it as if it is defined then the interrupt priority problem would get found immediately. http://www.freertos.org/a00110.html#configASSERT

Regarding debugging faults. Some IDE’s will do that for you. For example, Atollic’s TrueStudio. I don’t know about Sil Labs IDE though. This page may help if not:
http://www.freertos.org/Debugging-Hard-Faults-On-Cortex-M-Microcontrollers.html

Regards.

tantonini wrote on Monday, June 08, 2015:

Hi :slight_smile:

Thank you for your very quick answers.

You’re right, I had a problem with the priority. If I have well understood, to use ISR functions, the interrupt priority must be higher than configMAX_SYSCALL_INTERRUPT_PRIORITY.
In my case, configMAX_SYSCALL_INTERRUPT_PRIORITY = 95, so for my Cortex-M it is a priority of 5 (there are 4 priority bits), so, the interrupt priority must be lower than 5 (because lower value is higher priority on the Cortex-M) and the function NVIC_SetPriority expects the unshifted value as you said.

I wanted to define configASSERT(), but the line
#define configASSERT( ( x ) ) if( ( x ) == 0 ) vAssertCalled( FILE, LINE ) returns this error :
“(” may not appear in macro parameter list

I tried this one : #define configASSERT( x ) if( ( x ) == 0 ) vAssertCalled( FILE, LINE ) but there are 2 errors:
undefined reference to configASSERT' and undefined reference to vAssertCalled’ in the file queue.c

Have you an idea to resolve this error?

About the Mutex, I will follow your recommandations and add a finite block time.

Regards,

Thomas

rtel wrote on Monday, June 08, 2015:

vAssertCalled() is a function you would have to provide yourself, so you
can implement it to do what you want. You could alternative just switch
interrupts off and sit in a loop without calling a function:

#define configASSERT( x ) if( ( x ) == 0 ) { taskDISABLE_INTERRUPTS(); 
for( ;; ); }

Regards.